克隆代码

git clone https://github.com/ElemeFE/element.git

慢慢等吧,看运气了

从最新的发布分支上切一个分支出来

git tag

看到所有的发布之后的分支打的tag,v2.13.0是最新的

git checkout v2.13.0
git checkout -b reading

目录结构overview

- build项目构建命令的目录
- examples文档目录
- packages各个组件的源码目录
- src源码目录
- test测试目录
- types typescript定义的类型的目录
- .babelrc babel的配置
- .eslintignore eslint忽略的配置
- .eslintrc eslint的配置文件
- .travis.yml 持续集成的配置文件
- components.json 全部组建的列表
- package.json

看一个node项目最好的入口当然是package.json中的scripts了,首先看看package.json吧

package.json

下面是scripts节的内容

"bootstrap": "yarn || npm i", // 拿到项目源码第一步安装项目依赖包
    "build:file": "node build/bin/iconInit.js & node build/bin/build-entry.js & node build/bin/i18n.js & node build/bin/version.js",
    "build:theme": "node build/bin/gen-cssfile && gulp build --gulpfile packages/theme-chalk/gulpfile.js && cp-cli packages/theme-chalk/lib lib/theme-chalk",
    "build:utils": "cross-env BABEL_ENV=utils babel src --out-dir lib --ignore src/index.js",
    "build:umd": "node build/bin/build-locale.js",
    "clean": "rimraf lib && rimraf packages/*/lib && rimraf test/**/coverage",
    "deploy:build": "npm run build:file && cross-env NODE_ENV=production webpack --config build/webpack.demo.js && echo element.eleme.io>>examples/element-ui/CNAME",
    "deploy:extension": "cross-env NODE_ENV=production webpack --config build/webpack.extension.js",
    "dev:extension": "rimraf examples/extension/dist && cross-env NODE_ENV=development webpack --watch --config build/webpack.extension.js",
    "dev": "npm run bootstrap && npm run build:file && cross-env NODE_ENV=development webpack-dev-server --config build/webpack.demo.js & node build/bin/template.js",
    "dev:play": "npm run build:file && cross-env NODE_ENV=development PLAY_ENV=true webpack-dev-server --config build/webpack.demo.js",
    "dist": "npm run clean && npm run build:file && npm run lint && webpack --config build/webpack.conf.js && webpack --config build/webpack.common.js && webpack --config build/webpack.component.js && npm run build:utils && npm run build:umd && npm run build:theme",
    "i18n": "node build/bin/i18n.js",
    "lint": "eslint src/**/* test/**/* packages/**/* build/**/* --quiet", // 没啥好说的
    "pub": "npm run bootstrap && sh build/git-release.sh && sh build/release.sh && node build/bin/gen-indices.js && sh build/deploy-faas.sh",
    "test": "npm run lint && npm run build:theme && cross-env CI_ENV=/dev/ BABEL_ENV=test karma start test/unit/karma.conf.js --single-run",
    "test:watch": "npm run build:theme && cross-env BABEL_ENV=test karma start test/unit/karma.conf.js"
build:file

看下它的命令

"node build/bin/iconInit.js & node build/bin/build-entry.js & node build/bin/i18n.js & node build/bin/version.js"

都在build/bin目录下
node build/bin/iconInit.js
从packages/theme-chalk/src/icon.scss文件中提取字体图标的类名到examples/icon.json中

node build/bin/build-entry.js
根据components.json中组件的列表,结合字符串模板生成src/index.js,这是组件库的入口文件

node build/bin/i18n.js
为examples/pages中的模板文件生成不同语言的vue文件到examples/pages/[语言]/

node build/bin/version.js
生成版本字符串数组到examples/version.json中,文档会使用

现在总结一下build:file的作用
- 根据packages/theme-chalk/src/icon.scss生成examples/icon.json
- 根据components.json生成src/index.js
- 根据examples/i18n/page.json生成不同语言的文档框架组件
- 生成examples/version.json

除了第二个其他都是为了文档做准备的。

build:theme
"node build/bin/gen-cssfile && gulp build --gulpfile packages/theme-chalk/gulpfile.js && cp-cli packages/theme-chalk/lib lib/theme-chalk"

node build/bin/gen-cssfile
生成样式文件的入口文件index.scss,在packages/theme-chalk/src/index.scss
支持添加新的主题,添加主题的工作量不小的,需要为每个组件都提供一个新的样式文件,参照packages/theme-chalk

gulp build --gulpfile packages/theme-chalk/gulpfile.js
编译scss

cp-cli packages/theme-chalk/lib lib/theme-chalk
把编译好的样式文件拷贝到lib/theme-chalkk里面

总结一下build:theme,从名字上就能看出来这个是跟样式相关的命令,把scss文件编程css文件,并做相应的拷贝工作。

build:utils
"cross-env BABEL_ENV=utils babel src --out-dir lib --ignore src/index.js"

cross-env BABEL_ENV=utils babel src --out-dir lib --ignore src/index.js

build:umd
"node build/bin/build-locale.js"

node build/bin/build-locale.js
把ES6的模块定义转换成umd的模块定义,输出到lib/umd/locale中

clean
"rimraf lib && rimraf packages/*/lib && rimraf test/**/coverage"

rimraf lib
rimraf packages/*/lib
rimraf test/**/coverage

deploy:build
"npm run build:file && cross-env NODE_ENV=production webpack --config build/webpack.demo.js && echo element.eleme.io>>examples/element-ui/CNAME"

webpack --config build/webpack.demo.js
就是一个标准的webpack配置文件。这个其实是文档构建的webpack配置,非得加个demo,让人摸不着头脑!!

入口文件是examples/entry.js
输出目录是examples/element-ui

有一点要说一下,就是组件的文档使用md写的,需要先把md文件转换成vue文件,项目自己写了一个md的loader,厉害了

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

所有逻辑都做在了md-loader/index.js里面了,要完全搞明白代码有点复杂,先放一下。

echo element.eleme.io>>examples/element-ui/CNAME 不知道想干啥

总结一下,这个命令是构建文档的。

deploy:extension
"cross-env NODE_ENV=production webpack --config build/webpack.extension.js"

webpack --config build/webpack.extension.js

编译出一个chrome扩展插件,可以在浏览器中直接改element组件的颜色,挺有意思。
不过在执行这个命令的时候总报错,我没有解决它,只是把cross-env NODE_ENV=production干掉了

element UI CMS模板 element-ui源码_ico

dev:extension
"rimraf examples/extension/dist && cross-env NODE_ENV=development webpack --watch --config build/webpack.extension.js"

参考deploy:extension

dev
"npm run bootstrap && npm run build:file && cross-env NODE_ENV=development webpack-dev-server --config build/webpack.demo.js & node build/bin/template.js"
dev:play
"npm run build:file && cross-env NODE_ENV=development PLAY_ENV=true webpack-dev-server --config build/webpack.demo.js"

dist

"npm run clean && npm run build:file && npm run lint && webpack --config build/webpack.conf.js && webpack --config build/webpack.common.js && webpack --config build/webpack.component.js && npm run build:utils && npm run build:umd && npm run build:theme"
i18n
"node build/bin/i18n.js"

参考build:file

lint
"eslint src/**/* test/**/* packages/**/* build/**/* --quiet"

没啥好说的

pub
"npm run bootstrap && sh build/git-release.sh && sh build/release.sh && node build/bin/gen-indices.js && sh build/deploy-faas.sh"
test
"npm run lint && npm run build:theme && cross-env CI_ENV=/dev/ BABEL_ENV=test karma start test/unit/karma.conf.js --single-run"

用karma来做测试的

test:watch
"npm run build:theme && cross-env BABEL_ENV=test karma start test/unit/karma.conf.js"

到此package.json中的scripts基本过了一遍了。

怎么创建一个组件

- 编写组件的代码
- 为组件创建文档
- 为组件创建单元测试

编写组件

这里以一个hello-world组件为例。效果如下图,接收一个属性name,输出Hello, {{ name }}

1. 在packages下创建hello-world目录
2. 在hello-world中创建index.js和src/main.vue
```
// index.js
import HelloWorld from './src/main';
/* istanbul ignore next */
HelloWorld.install = function(Vue) {
  Vue.component(HelloWorld.name, HelloWorld);
};
export default HelloWorld;

// src/main.vue
<template>
  <div class="hello-world">
    Hello, <a href="#">{{ name }}</a>
  </div>
</template>
<script>
export default {
  name: 'HelloWorld',
  props: ['name'],
  data() {
    return {};
  }
};
</script>
```
3. 在components.json中注册hello-world组件
```
"hello-world": "./packages/hello-world/index.js"
```
4. 在packages/theme/chalk/src下添加hello-world.scss
	```
	.hello-world {
	
	}
	```

添加文档

在examples/docs/zh-CN下添加hello-world.md
在nav.config.json中注册

添加测试

在test/unit下创建hello-world.spec.js

import { createTest } from '../util';
import HelloWorld from 'packages/hello-world';
describe.only('test HelloWorld', () => {
  const name = '岁月神偷';
  const vm = createTest(HelloWorld, {
    name
  }, true);
  it('there should be .hello-world', () => {
    expect(vm.$el.classList.contains('hello-world')).to.be.true;
  });
  it('should contains name', () => {
    expect(vm.$el.textContent).to.contains(name);
  });
});

npm run test:watch 运行
因为单元测试太多了,我们只想测试自己的组件,所以上面的测试我加了only,这样就只会跑这个测试了,等开发完组件,要把这个干掉的。

这里用的断言库好像是should.js,感兴趣的自己查查看。