一、 Makefile 文件

Make是最常用的构建工具,它的规则配置一般写在 Makefile 文件里。make 的使用有点像 package.json 里的 scripts,把一段长命令用一段短命令来执行。

Makefile文件由一系列规则(rules)构成。每条规则的形式如下。

<target> : <prerequisites> [tab] <commands>

上面第一行冒号前面的部分,叫做"目标"(target),冒号后面的部分叫做"前置条件"(prerequisites);第二行必须由一个tab键起首,后面跟着"命令"(commands)。

Element UI 项目里的 Makefile 代码如下:

# Makefile中,.PHONY后面的target表示的也是一个伪造的target, 而不是真实存在的文件target,注意Makefile的target默认是文件。
.PHONY: dist test
# 执行make默认指向help
default: help
# 构建主题
# build all theme
build-theme:
    npm run build:theme
# 安装依赖
install:
    npm install
# 使用淘宝源安装依赖
install-cn:
    npm install --registry=http://registry.npm.taobao.org
# 构建应用
dev:
    npm run dev

play:
    npm run dev:play
# 新增组件
new:
    node build/bin/new.js $(filter-out $@,$(MAKECMDGOALS))
# 为网站添加新语言
new-lang:
    node build/bin/new-lang.js $(filter-out $@,$(MAKECMDGOALS))
# 打包
dist: install
    npm run dist
# 部署
deploy:
    @npm run deploy
# 发布
pub:
    npm run pub
# 单元测试
test:
    npm run test:watch
# 帮助信息
help:
    @echo "   \033[35mmake\033[0m \033[1m命令使用说明\033[0m"
    @echo "   \033[35mmake install\033[0m\t\033[0m\t\033[0m\t\033[0m\t---  安装依赖"
    @echo "   \033[35mmake new <component-name> [中文名]\033[0m\t---  创建新组件 package. 例如 'make new button 按钮'"
    @echo "   \033[35mmake dev\033[0m\t\033[0m\t\033[0m\t\033[0m\t---  开发模式"
    @echo "   \033[35mmake dist\033[0m\t\033[0m\t\033[0m\t\033[0m\t---  编译项目,生成目标文件"
    @echo "   \033[35mmake deploy\033[0m\t\033[0m\t\033[0m\t\033[0m\t---  部署 demo"
    @echo "   \033[35mmake pub\033[0m\t\033[0m\t\033[0m\t\033[0m\t---  发布到 npm 上"
    @echo "   \033[35mmake new-lang <lang>\033[0m\t\033[0m\t\033[0m\t---  为网站添加新语言. 例如 'make new-lang fr'"

除了 make new 和 make new-lang 两个命令外,其他脚本命令都已经分析过了。

make new

node build/bin/new.js $(filter-out $@,$(MAKECMDGOALS))

执行该命令可以新增组件。

可以执行 "make new aaa 组件中文名" 或者 "node ./build/bin/new.js aaa 组件中文名" 来生成 aaa 组件,方便快捷多了。

添加新组件 aaa,优势出来了:

  1. 在/packages目录下新建组件目录,并完成目录结构的构建/packages/aaa
  2. 创建组件文档,/examples/docs/{lang}/aaa.md
  3. 创建组件单元测试文件,/test/unit/specs/aaa.spec.js
  4. 创建组件样式文件,/packages/theme-chalk/src/aaa.scss
  5. 创建组件类型声明文件,/types/aaa.d.ts
  6. 配置: 在 /components.json 文件中配置组件信息, 在 /examples/nav.config.json 中添加该组件的路由配置, 在 /packages/theme-chalk/src/index.scss 文件中自动引入该组件的样式文件, 将类型声明文件在 /types/element-ui.d.ts 中自动引入

make new-lang

node build/bin/new-lang.js $(filter-out $@,$(MAKECMDGOALS))

执行该命令后添加新语言。

二、Element 官网左侧路由

生成路由文件:examples/route.config.js

除了【更新日志】的路由组件在 examples/pages/zh-CN/ 目录下以外,所有组件的路由组件都在 examples/docs/zh-CN/ 目录下,以 .md 形式存在。

还有一个开发时,调试组件用的特殊路由 /play。

三、更新图标字体库

对 Element 二次开发时,我们有时候要添加 Icon 图标。我们怎么来维护这些图标呢?

步骤如下:

  1. 在 iconfont.cn 新建一个项目用来维护图标库
  2. 添加、修改图标后,选择“Font class”类型,点击项目设置更改前缀为“el-icon-”,然后点击“下载至本地”

element select组件封装支持搜索_官网

    3.  替换 packages/theme-chalk/src/fonts 目录下的字体文件(ttf、woff)

    4. 把 packages/theme-chalk/src/icon.scss 最底部的这段代码移到上面来,方便以后更新。

element select组件封装支持搜索_ico_02

    5. 更新 packages/theme-chalk/src/icon.scss 文件(看一下第49行注释)

    6. 更新 examples/icon.json

    7. 重新打包:npm run dist

四、其他工程化脚本

md-loader

说到 md-loader,官网的文档展示和 demo 展示多亏了他。

它是一个 loader,官网组件页面的组件 demo 加文档的模式一大半的功劳都是源自于它。

可以在 /examples/route.config.js 中看到 registerRoute 方法生成组件页面的路由配置时,使用 loadDocs 方法加载 /examples/docs/zh-CN/组件名.md

注意,这里加载的是 markdown 文档,而不是 vue 文件,但是却能像 vue 文件一样在页面上渲染成一个 Vue 组件,这是怎么做到的呢?

我们知道,webpack 的理念是一切资源都可以 require,只需配置相应的 loader 即可。在 /build/webpack.demo.js 文件中的 module.rules 下可以看到对 markdown 规则的处理,先通过 md-loader 处理 markdown 文件,从中解析出 vue 代码,然后交给 vue-loader,最终生成 vue 单文件组件渲染到页面。这就能看到组件页面的文档和组件 demo 展示效果。

/build/webpack.demo.js

{
        test: /\.md$/,
        use: [
          {
            loader: 'vue-loader',
            options: {
              compilerOptions: {
                preserveWhitespace: false
              }
            }
          },
          {
            loader: path.resolve(__dirname, './md-loader/index.js')
          }
        ]
      },

至于如何将 markdown 解析成 vue 组件,可以阅读下 Element 团队的:谈谈 Element 文档中的 Markdown 解析

build/config.js  

webpack 的公共配置,比如 externals、alias 等。通过 externals 的配置解决了组件库部分代码的冗余问题,比如组件和组件库公共模块的代码,但是组件样式冗余问题没有得到解决;alias 别名配置为开发组件库提供了方便。

// webpack 公共配置,比如 externals、alias
var path = require('path');
var fs = require('fs');
var nodeExternals = require('webpack-node-externals');
var Components = require('../components.json');

Object.keys(Components).forEach(function (key) {
  externals[`element-ui/packages/${key}`] = `element-ui/lib/${key}`;
});
// externals 解决组件依赖其它组件并按需引入时代码冗余的问题
// 比如 Table 组件依赖 Checkbox 组件,在项目中如果我同时引入 Table 和 Checkbox 时,会不会产生冗余代码
// 如果没有以下内容的的话,会,这时候你会看到有两份 Checkbox 组件代码。
// 包括 locale、utils、mixins、transitions 这些公共内容,也会出现冗余代码
// 但有了 externals 的设置,就会将告诉 webpack 不需要将这些 import 的包打包到 bundle 中,运行时再从外部去
// 获取这些扩展依赖。这样就可以在打包后 /lib/tables.js 中看到编译后的 table.js 对 Checkbox 组件的依赖引入:
// module.exports = require("element-ui/lib/checkbox")
// 这么处理之后就不会出现冗余的 JS 代码,但是对于 CSS 部分,element-ui 并未处理冗余情况。
// 可以看到 /lib/theme-chalk/table.css 和 /lib/theme-chalk/checkbox.css 中都有 Checkbox 组件的样式

externals['element-ui/src/locale'] = 'element-ui/lib/locale';
utilsList.forEach(function (file) {
  file = path.basename(file, '.js');
  externals[`element-ui/src/utils/${file}`] = `element-ui/lib/utils/${file}`;
});
mixinsList.forEach(function (file) {
  file = path.basename(file, '.js');
  externals[`element-ui/src/mixins/${file}`] = `element-ui/lib/mixins/${file}`;
});
transitionList.forEach(function (file) {
  file = path.basename(file, '.js');
  externals[`element-ui/src/transitions/${file}`] = `element-ui/lib/transitions/${file}`;
});

externals = [Object.assign({
  vue: 'vue'
}, externals), nodeExternals()];

exports.externals = externals;
// 设置别名
exports.alias = {
  main: path.resolve(__dirname, '../src'),
  packages: path.resolve(__dirname, '../packages'),
  examples: path.resolve(__dirname, '../examples'),
  'element-ui': path.resolve(__dirname, '../')
};

exports.vue = {
  root: 'Vue',
  commonjs: 'vue',
  commonjs2: 'vue',
  amd: 'vue'
};

exports.jsexclude = /node_modules|utils\/popper\.js|utils\/date\.js/;

总结

Element 整体架构是真的非常棒,利用脚本实现工程化,值得我们在开发中学习和应用。涉及到添加新组件、添加新语言、构建应用、打包编译应用、发布应用、单元测试等等。比如添加新组件,执行完脚本可以帮助开发者解决创建新组件的目录、四种语言下的文档、配置官网展示文件等。

开发者只需要编写具体组件的逻辑即可,根本不需要每一次新建一个组件改动多个文件,拒绝重复劳动。切记,在项目搭建的过程中,可以使用脚本解决的事情,坚决不要手动解决。

参考资料