背景
当前项目为vue 2.6 + element-ui 2.14.1, 我们需要开发一个类似表格的表单组件, 可以看到除了表格样式以外,我们还需要嵌套各种表单组件,而组件功能基本与框架功能一致,如果对每个组件都做独立开发,显然是不现实的。所以我们的目标一定是尽量使用原组件
- 方案一: 使用容器组件或自定义类名, 覆盖原组件样式。
- 方案二:将element拉到本地,做二次开发
这里我们选择了第二种。
概况
因为项目依赖,所以我们这里拉取的是 element-ui 2.14.1, 如需要其他版本 切换不同的 tag 标签分支即可。
命令
二次开发前我们需要先将项目跑起来,所以需要了解基础的命令
"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",
// 组件打包, 将在本地生成 lib/ 目录
"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"
},
这里我们一般只用到了,dev
dist
以及后面我们一个自定命令。
项目目录
- root/
- program/
- element-ui/
element目录概览
- build/ 构建配置 | 构建脚本
- examples/ 文档, 组件用例
- src/ 项目源码| 项目入口
- packages/ 组件源码
- types/ ts类型定义
这里主要关注 examples/, packages/ , 这与我们开发直接相关。
examples/
这里其实是个vue项目, dev
将运行该工程。
- examples/
- docs/ 组件文档 | 组件用例
- zh-CN/ 中文包
- en-US/ 英文包
- .... 其他语言包
- pages/ 页面模板
- i18n/ 国际化
- components/ 页面公共组件
- extension/ 扩展
- entry.js 项目入口
- nav.config.json 导航配置
- router.config.js vue 路由加载器
- docs/ 组件文档 | 组件用例
packages/
这里是我们编写组件的地方,所以组件都以独立目录包的形式存在,方便按需加载。
- 约定
每个包遵守基础的包结构
\- package
- index.js 导出入口
- src/ 源
- 样式文件
看过组件包后, 会发现包内是不包含样式文件的,样式文件放在了 /packages/theme-chalk/
目录下。
所以其实 element-ui 的样式作为独立的主题包存在。
开发自定组件
这里我们以 row
为例子,通过在源组件基础上修改一个自己的新组件 z-row
。
新建组件目录
// 拷贝row 目录 并重命名为 z-row
mv row - 副本 z-row
// 修改组件导出
// index.js
import ZRow from './src/z-row';
/\* istanbul ignore next \*/
ZRow.install = function(Vue) {
Vue.component(ZRow.name, ZRow);
};
export default ZRow;
// 修改源文件名
// z-row/src/z-row
修改组件
export default {
name: 'ZRow',
componentName: 'ZRow', // 修改组件名
props: {
tag: {
type: String,
default: 'div'
},
gutter: Number,
type: String,
justify: {
type: String,
default: 'start'
},
align: {
type: String,
default: 'top'
}
},
computed: {
style() {
const ret = {};
if (this.gutter) {
ret.marginLeft = \`-${this.gutter / 2}px\`;
ret.marginRight = ret.marginLeft;
}
return ret;
}
},
render(h) {
return h(this.tag, {
class: \[
'z-row', // 将源 el-row 类名 替换为 z-row, 并替换其他 el- 前缀
this.justify !== 'start' ? \`is-justify-${this.justify}\` : '',
this.align !== 'top' ? \`is-align-${this.align}\` : '',
{ 'z-row--flex': this.type === 'flex' }
\],
style: this.style
}, this.$slots.default);
修改样式
// 进入/packages/theme-chalk/src
// copy row.sccs 并更名为 z-row.sccs
// 修改样式
@import "common/var";
@import "mixins/mixins";
@import "mixins/utils";
// zb 为自定义组件前缀添加器, 具体的实现可以参考 mixins/mixings 的 @mixin b
@include zb(row) { // 生成类名 .z-row
position: relative;
box-sizing: border-box;
border: 1px solid #E5E5E5;
align-items: stretch;
& + &{
border-top: 0;
}
@include utils-clearfix;
@include m(flex) {
display: flex;
&:before,
&:after {
display: none;
}
@include when(justify-center) {
justify-content: center;
}
@include when(justify-end) {
justify-content: flex-end;
}
@include when(justify-space-between) {
justify-content: space-between;
}
@include when(justify-space-around) {
justify-content: space-around;
}
@include when(align-middle) {
align-items: stretch;
}
@include when(align-bottom) {
align-items: flex-end;
}
}
注册组件
打开根目录下的 components.json
{
// 添加新组件
"z-row": "./packages/z-row/index.js",
...
}
编写文档/用例
- 新增文档
// /examples/docs/zh-CN/ 新增 z-layout.md
## z-layout 布局
## 基础使用
\`\`\`html
// 编写组件用例
<z-row>
<z-col :span="24"><div class="grid-content bg-purple-dark"> content </div></z-col>
</z-row>
\`\`\`
- 注册路由
// examples/nav.config.json
// 在组件列表下新增目录配置
{
"groupName": "z-ui",
"list": \[
{
"path": "/z-layout",
"title": "z-layout| z-row | z-col"
}
\]
},
- 运行用例
yarn dev
```这样我们就可以看到element-ui 组件目录下新增了我们的自定义组件, 这里是支持热更新的,修改文档的同时,内容也将即时显示。
## 打包
这里我们希望依然以elemnt官方的方式使用本地的魔改包, 只是添加注册新增的自定义组件。开始使用了`lerna` 但是存在命名冲突的问题, lerna无法通过包名判断安装的是本地包还是线上包,如果只修改package.json 的 elemnt包名,将导致无法正常导入组件的问题, 因为还需要修改打包的配置,这样会比较的麻烦。 所以使用了hack方法, 直接替换项目下 node\_modules 内的 element-ui 文件, 其他也可以直接就在项目下引入已经构建好的 elemnt-ui 打包文件。
### 生成lib
```javascript
yarn dist // 生成
迁移脚本
每次打包后,导出新的包文件会很麻烦,所以可以使用gulp将打包后的文件导入到项目中.
const { series, src, dest } = require('gulp');
const packageJson = require('../package.json')
const path = require('path')
const resolve = \_path => path.resolve(\_\_dirname, \_path)
const move = (from, to) => cb => {
src(from)
.pipe(dest(to))
cb()
}
const buildTask = (files, target) => files.map(source => {
const from = resolve(\`../${source}/\*\*/\*\`)
const to = resolve(\`../${target}/${source}/\`)
return move(from, to)
})
function trucks(){
console.log(packageJson.files)
const task = buildTask(packageJson.files, packageJson.moveTarger)
console.log(task)
return series(...task)
}
module.exports.default = tr
总结
element-ui 2.0 感觉不太适合做二次开发,最直接的一点是前缀。