文章已收录到 github,欢迎 Watch 和 Star。

简介

在了解 Element 源码架构 的基础上,接下来我们基于 element-ui 为团队打造自己的组件库。

主题配置

基础组件库在 UI 结构上差异很小,一般只是在主题色上会有较大差异,毕竟每个团队都有了 UI 风格。比如,我们团队的 UI 设计稿其实是基于 Ant Design 来出的,而组件库是基于 Element-UI 来开发,即使是这种情况,对组件本身的改动也很少。所以,基于开源库打造团队自己的组件库时,主题配置就很重要了。

element-ui 的一大特色就是支持自定义主题,它通过在线主题编辑器、Chrome 插件或命令行主题工具这三种方式来定制 element-ui 所有组件的样式。那么 element-ui 是怎么做到这一点的呢?

因为 element-ui 组件样式中的颜色、字体、线条等样式都是通过变量的方式引入的,在 packages/theme-chalk/src/common/var.scss 中可以看到这些变量的定义,这就为自定义主题提供了方便,因为我们只需要修改这些变量,就可以实现组件主题的改变。

在线主题编辑器和 Chrome 插件支持实时预览。并且可以下载定制的样式包,然后使用。在线主题编辑器和 Chrome 插件的优点是可视化,简洁明了,但是有个最大的缺点就是,最后下载出来的是一个将所有组件样式打包到一起的样式包,没办法支持按需加载,不推荐使用。这里我们使用命令行主题工具来定制样式。

命令行主题工具

  • 初始化项目目录并安装主题生成工具(element-theme)

    mkdir theme && cd theme && npm init -y && npm i element-theme -D
  • 安装白垩主题

    npm i element-theme-chalk -D
  • 初始化变量文件

    node_modules/.bin/et -i

    命令执行以后可能会得到如下报错信息

    原因是 element-theme 包中依赖了低版本的 graceful-fs,低版本 graceful-fs 在高版本的 node.js 中不兼容,最简单的方案是升级 graceful-fs。

    在项目根目录下创建 npm-shrinkwrap.json 文件,并添加如下内容:

    {
    "dependencies": {
    "graceful-fs": {
    "version": "4.2.2"
    }
    }
    }

    运行 npm install 重新安装依赖即可解决,然后重新执行 node_modules/.bin/et -i,执行完以后会在当前目录生成 element-variables.scss 文件。

  • 修改变量

    直接编辑 element-variables.scss 文件,例如修改主题色为红色,将文件中的 $--color-primary 的值修改为 red$--color-primary: red !default;

    文件中写了很好的注释,并且样式代码也是按照组件来分割组织的,所以大家可以对照设计团队给到的设计稿来一一修改相关的变量。如果实在觉得看代码比较懵,可以参照在线主题编辑器,两边的变量名是一致的。

    题外话:element-ui 还提供了两个资源包,供设计团队使用,所以最理想的是,让设计团队根据 element-ui 的资源包出设计稿,这样两边就可以做到统一,研发团队的工作量也会降低不少。比如我们团队就不是这样,设计团队给到的设计稿是基于 Ant Design 出的,研发组件库时改动的工作量和难度就会相对比较大。所以研发、设计、产品一定要进行很好的沟通。

  • 编译主题

    修改完以后,保存文件,然后执行以下命令编译主题,会产生一个 theme 目录。生产出来都是 CSS 样式文件,文件名和组件名一一对应,支持按需引入(指定组件的样式文件)和全量引入(index.css)。

    • 生产未压缩的样式文件

      node_modules/.bin/et --out theme-chalk
    • 生产经过压缩的样式文件

      node_modules/.bin/et --minimize --out theme-chalk
    • 帮助命令

      node_modules/.bin/et --help
    • 启用 watch 模式,实时编译主题

      node_modules/.bin/et --watch --out theme-chalk
  • 使用自定义主题

    • 用新生成的主题目录(theme-chalk)替换掉框架中的 packages/theme-chalk 目录。重命名老的 theme-chalk 为 theme-chalk.bak,不要删掉,后面需要用

      建议将生成主题时用到的 element-variables.scss 文件保存在项目中,因为以后你可能会需要重新生成主题

    • 修改 /examples/entry.js/examples/play.js/examples/extension/src/app.js 中引入的组件库的样式

      // 用新的样式替换旧的默认样式
      // import 'packages/theme-chalk/src/index.scss
      import 'packages/theme-chalk/index.css
    • 修改 /build/bin/iconInit.js 中引入的图标样式文件

      // var fontFile = fs.readFileSync(path.resolve(__dirname, '../../packages/theme-chalk/src/icon.scss'), 'utf8');
      var fontFile = fs.readFileSync(path.resolve(__dirname, '../../packages/theme-chalk/icon.css'), 'utf8');
    • 修改 /examples/docs/{四种语言}/custom-theme.md

      // @import "~element-ui/packages/theme-chalk/src/index";
      @import "~element-ui/packages/theme-chalk/index";
    • 执行 make dev 启动开发环境,查看效果

    到这一步,主题配置就结束了,你会发现,element-ui 官网的组件样式基本上和设计稿上的一致。但是仔细对比后,会发现有一些组件的样式和设计稿有差异,这时候就需要对这些组件的样式进行深度定制,覆写不一致的样式。

    其实这块儿漏掉了 /build/bin/new.js 中涉及的样式目录,这块儿的改动会放到后面

样式深度定制

上一步的主题配置,只能解决主题相关的样式,但是有些组件的有些样式不属于主题样式,如果这部分样式刚好又和设计稿不一致的话,那就需要重写这部分样式去覆盖上一步的样式。

以下配置还支持为自定义组件添加样式

样式目录

  • 主题配置 步骤中备份的 /packages/theme-chalk.bak 重命名为 /packages/theme-lyn,作为覆写组件和自定义组件的样式目录

  • 删掉 /packages/theme-lyn/src 目录的所有文件

  • 你会写 scss ?

    • 忽略掉下一步,然后后续步骤你只需将对应的 less 操作换成 sass 即可
  • 你不会写 scss,扩展其它语法,假设你会写 less

    • 在项目根目录执行以下命令,然后删掉 gulp-sass

      npm i less less-loader gulp-less -D && npm uninstall gulp-sass -D

      如果一会儿启动开发环境以后,报错 “TypeError: this.getOptions is not a function”,则降级 less-loader 版本,比如我的版本是:less@3.7.1、less-loader@7.3.0

    • /packages/theme-lyn 目录下执行以下命令,然后删掉 gulp-sass

      npm i gulp-less -D && npm uninstall gulp-sass -D
    • /packages/theme-lyn/gulpfile.js 更改为以下内容

      'use strict';
      
      /**
      * 将 ./src/*.less 文件编译成 css 文件输出到 ./lib 目录
      * 将 ./src/fonts/中的所有字体文件输出到 ./lib/fonts 中,如果你没有覆写字体样式的需要,则删掉拷贝字体样式部分
      */
      const { series, src, dest } = require('gulp');
      const less = require('gulp-less');
      const autoprefixer = require('gulp-autoprefixer');
      const cssmin = require('gulp-cssmin');
      const path = require('path') function compile() {
      return src('./src/*.less')
      .pipe(less({
      paths: [ path.join(__dirname, './src') ]
      }))
      .pipe(autoprefixer({
      browsers: ['ie > 9', 'last 2 versions'],
      cascade: false
      }))
      .pipe(cssmin())
      .pipe(dest('./lib'));
      } function copyfont() {
      return src('./src/fonts/**')
      .pipe(cssmin())
      .pipe(dest('./lib/fonts'));
      } // 也可以在这里扩展其它功能,比如拷贝静态资源 exports.build = series(compile, copyfont);
    • build/webpack.demo.js 中增加解析 less 文件的规则

      {
      test: /\.less$/,
      use: [
      isProd ? MiniCssExtractPlugin.loader : 'style-loader',
      'css-loader',
      'less-loader'
      ]
      }
  • 假如你要覆写 button 组件的部分样式

    • /packages/theme-lyn/src 目录下新建 button.less 文件,编写覆写样式时请遵循如下规则

      • 组件样式的覆写,最好遵循 BEM 风格,目的是提供良好的命名空间隔离,避免样式打包以后发生意料之外的覆盖
      • 只覆写已有的样式,可以在组件上新增类名,但不要删除,目的是兼容线上代码
      // 这里我要把主要按钮的字号改大有些,只是为了演示效果
      .el-button--primary {
      font-size: 24px;
      }
  • 改造 build/bin/gen-cssfile.js 脚本

    /**
    * 将各个覆写的样式文件在 packages/theme-lyn/src/index.less 文件中自动引入
    */ var fs = require('fs');
    var path = require('path'); // 生成 theme-lyn/src 中的 index.less 文件
    function genIndexLessFile(dir) {
    // 文件列表
    const files = fs.readdirSync(dir);
    /**
    * @import 'x1.less';
    * @import 'x2.less;
    */
    let importStr = "/* Automatically generated by './build/bin/gen-cssfile.js' */\n"; // 需要排除的文件
    const excludeFile = ['assets', 'font', 'index.less', 'base.less', 'variable.less']; files.forEach(item => {
    if (excludeFile.includes(item) || !/\.less$/.test(item)) return; // 只处理非 excludeFile 中的 less 文件
    importStr += `@import "./${item}";\n`;
    }); // 在 packages/theme-lyn/src/index.less 文件中写入 @import "xx.less",即在 index.less 中引入所有的样式文件
    fs.writeFileSync(path.resolve(dir, 'index.less'), importStr);
    } genIndexLessFile(path.resolve(__dirname, '../../packages/theme-lyn/src/'));
  • 在项目根目录下执行以下命令

    npm i shelljs -D
  • 新建 /build/bin/compose-css-file.js

    /**
    * 负责将打包后的两个 css 目录(lib/theme-chalk、lib/theme-lyn)合并
    * lib/theme-chalk 目录下的样式文件是通过主题配置自动生成的
    * lib/theme-lyn 是扩展组件的样式(覆写默认样式和自定义组件的样式)
    * 最后将样式都合并到 lib/theme-chalk 目录下
    */
    const fs = require('fs');
    const fileSave = require('file-save');
    const { resolve: pathResolve } = require('path');
    const shelljs = require('shelljs'); const themeChalkPath = pathResolve(__dirname, '../../lib/theme-chalk');
    const themeStsUIPath = pathResolve(__dirname, '../../lib/theme-lyn'); // 判断样式目录是否存在
    let themeChalk = null;
    let themeStsUI = null;
    try {
    themeChalk = fs.readdirSync(themeChalkPath);
    } catch (err) {
    console.error('/lib/theme-chalk 不存在');
    process.exit(1);
    }
    try {
    themeStsUI = fs.readdirSync(themeStsUIPath);
    } catch (err) {
    console.error('/lib/theme-lyn 不存在');
    process.exit(1);
    } /**
    * 遍历两个样式目录,合并相同文件,将 theme-lyn 的中样式追加到 theme-chalk 中对应样式文件的末尾
    * 如果 theme-lyn 中的文件在 theme-chalk 中不存在(比如扩展的新组件),则直接将文件拷贝到 theme-chalk
    */
    const excludeFiles = ['element-variables.css', 'variable.css'];
    for (let i = 0, themeStsUILen = themeStsUI.length; i < themeStsUILen; i++) {
    if (excludeFiles.includes(themeStsUI[i])) continue; if (themeStsUI[i] === 'fonts') {
    shelljs.cp('-R', pathResolve(themeStsUIPath, 'fonts/*'), pathResolve(themeChalkPath, 'fonts'));
    continue;
    } if (themeStsUI[i] === 'assets') {
    shelljs.cp('-R', pathResolve(themeStsUIPath, 'assets'), themeChalkPath);
    continue;
    } if (themeChalk.includes(themeStsUI[i])) {
    // 说明当前样式文件是覆写 element-ui 中的样式
    const oldFileContent = fs.readFileSync(pathResolve(themeChalkPath, themeStsUI[i]), { encoding: 'utf-8' });
    fileSave(pathResolve(themeChalkPath, themeStsUI[i])).write(oldFileContent).write(fs.readFileSync(pathResolve(themeStsUIPath, themeStsUI[i])), 'utf-8').end();
    } else {
    // 说明当前样式文件是扩展的新组件的样式文件
    // fs.writeFileSync(pathResolve(themeChalkPath, themeStsUI[i]), fs.readFileSync(pathResolve(themeStsUIPath, themeStsUI[i])));
    shelljs.cp(pathResolve(themeStsUIPath, themeStsUI[i]), themeChalkPath);
    }
    } // 删除 lib/theme-lyn
    shelljs.rm('-rf', themeStsUIPath);
  • 改造 package.json 中的 scripts

    {
    "gen-cssfile:comment": "在 /packages/theme-lyn/src/index.less 中自动引入各个组件的覆写样式文件",
    "gen-cssfile": "node build/bin/gen-cssfile",
    "build:theme:comment": "构建主题样式:在 index.less 中自动引入各个组件的覆写样式文件 && 通过 gulp 将 less 文件编译成 css 并输出到 lib 目录 && 拷贝基础样式 theme-chalk 到 lib/theme-chalk && 拷贝 编译后的 theme-lyn/lib/* 目录到 lib/theme-lyn && 合并 theme-chalk 和 theme-lyn",
    "build:theme": "npm run gen-cssfile && gulp build --gulpfile packages/theme-lyn/gulpfile.js && cp-cli packages/theme-lyn/lib lib/theme-lyn && cp-cli packages/theme-chalk lib/theme-chalk && node build/bin/compose-css-file.js",
    }
  • 执行以下命令

    npm run gen-cssfile
  • 改造 /examples/entry.js/examples/play.js

    // 用新的样式替换旧的默认样式
    // import 'packages/theme-chalk/src/index.scss
    import 'packages/theme-chalk/index.css // 在这行下面引入自定义样式
    // 引入自定义样式
    import 'packages/theme-lyn/src/index.less'
  • 访问官网,查看 button 组件的覆写样式是否生效

自定义组件

组件库在后续的开发和迭代中,需要两种自定义组件的方式:

  • 增加新的 element-ui 组件

    element-ui 官网可能在某个时间点增加一个你需要的基础组件,这时你需要将其集成进来

  • 增加业务组件

    基础组件就绪以后,团队就会开始推动业务组件的建设,这时候就会向组件库中增加新的组件

新的 element-ui 组件

element-ui 提供了增加新组件的脚本,执行 make new <component-name> [中文名] 即可生成新组件所需的所有文件以及配置,比如:make new button 按钮,有了该脚本可以让你专注于组件的编写,不需要管任何配置。

/build/bin/new.js

但是由于我们调整了框架主题库的结构,所以脚本文件也需要做相应的调整。需要将 /build/bin/new.js 文件中处理样式的代码删掉,样式文件不再需要脚本自动生成,而是通过重新生成主题的方式实现。

// /build/bin/new.js 删掉以下代码
{
filename: path.join('../../packages/theme-chalk/src', `${componentname}.scss`),
content: `@import "mixins/mixins";
@import "common/var"; @include b(${componentname}) {
}`
}, // 添加到 index.scss
const sassPath = path.join(__dirname, '../../packages/theme-chalk/src/index.scss');
const sassImportText = `${fs.readFileSync(sassPath)}@import "./${componentname}.scss";`;
fileSave(sassPath)
.write(sassImportText, 'utf8')
.end('\n');

Makefile

改造 Makefile 文件,在 new 配置后面增加 && npm run build:file 命令,重新生成组件库入口文件,不然不会引入新增加的组件。

new:
node build/bin/new.js $(filter-out $@,$(MAKECMDGOALS)) && npm run build:file

增加新组件

完成上述改动以后,只需两步即可完成新 element-ui 组件的创建:

  • 执行 make new <component-name> [组件中文名] 命令新建新的 element-ui 组件

    这一步会生成众多文件,你只需要从新的 element-ui 源码中将该组件对应的代码复制过来填充到对应的文件即可

  • 重新生成主题,然后覆盖现在的 /packages/theme-chalk

业务组件

新增的业务组件就不要以 el 开头了,避免和 element 组件重名或造成误会。需要模拟 /build/bin/new.js 脚本写一个新建业务组件的脚本 /build/bin/new-lyn-ui.js,大家可以基于该脚本去扩展。

/build/bin/new-lyn-ui.js

'use strict';

/**
* 新建组件脚本,以 lyn-city 组件为例
* 1、在 packages 目录下新建组件目录,并完成目录结构的基本创建
* 2、创建组件文档
* 3、组件单元测试文件
* 4、组件样式文件
* 5、组件类型声明文件
* 6、并将上述新建的相关资源自动添加的相应的文件,比如组件组件注册到 components.json 文件、样式文件在 index.less 中自动引入等
* 总之你只需要专注于编写你的组件代码即可,其它一概不用管
*/ console.log();
process.on('exit', () => {
console.log();
}); if (!process.argv[2]) {
console.error('[组件名]必填 - Please enter new component name');
process.exit(1);
} const path = require('path');
const fs = require('fs');
const fileSave = require('file-save');
const uppercamelcase = require('uppercamelcase');
// 组件名称 city
const componentname = process.argv[2];
// 组件中文名 城市列表
const chineseName = process.argv[3] || componentname;
// 组件大驼峰命名 City
const ComponentName = uppercamelcase(componentname);
// 组件路径:/packages/city
const PackagePath = path.resolve(__dirname, '../../packages', componentname);
const Files = [
// packages/city/index.js 的内容
{
filename: 'index.js',
content: `import ${ComponentName} from './src/main'; /* istanbul ignore next */
${ComponentName}.install = function(Vue) {
Vue.component(${ComponentName}.name, ${ComponentName});
}; export default ${ComponentName};`
},
// packages/city/src/main.vue 组件定义
{
filename: 'src/main.vue',
content: `<template>
<div class="lyn-${componentname}"></div>
</template> <script>
export default {
name: 'Lyn${ComponentName}'
};
</script>`
},
// 组件中文文档
{
filename: path.join('../../examples/docs/zh-CN', `${componentname}.md`),
content: `## ${ComponentName} ${chineseName}`
},
// 组件单元测试文件
{
filename: path.join('../../test/unit/specs', `${componentname}.spec.js`),
content: `import { createTest, destroyVM } from '../util';
import ${ComponentName} from 'packages/${componentname}'; describe('${ComponentName}', () => {
let vm;
afterEach(() => {
destroyVM(vm);
}); it('create', () => {
vm = createTest(${ComponentName}, true);
expect(vm.$el).to.exist;
});
});
`
},
// 组件样式文件
{
filename: path.join(
'../../packages/theme-lyn/src',
`${componentname}.less`
),
content: `@import "./base.less";\n\n.lyn-${componentname} {
}`
},
// 组件类型声明文件
{
filename: path.join('../../types', `${componentname}.d.ts`),
content: `import { LynUIComponent } from './component' /** ${ComponentName} Component */
export declare class Lyn${ComponentName} extends LynUIComponent {
}`
}
]; // 将新组件添加到 components.json
const componentsFile = require('../../components.json');
if (componentsFile[componentname]) {
console.error(`${componentname} 已存在.`);
process.exit(1);
}
componentsFile[componentname] = `./packages/${componentname}/index.js`;
fileSave(path.join(__dirname, '../../components.json'))
.write(JSON.stringify(componentsFile, null, ' '), 'utf8')
.end('\n'); // 在 index.less 中引入新组件的样式文件
const lessPath = path.join(
__dirname,
'../../packages/theme-lyn/src/index.less'
);
const lessImportText = `${fs.readFileSync(
lessPath
)}@import "./${componentname}.less";`;
fileSave(lessPath).write(lessImportText, 'utf8').end('\n'); // 添加到 element-ui.d.ts
const elementTsPath = path.join(__dirname, '../../types/element-ui.d.ts'); let elementTsText = `${fs.readFileSync(elementTsPath)}
/** ${ComponentName} Component */
export class ${ComponentName} extends Lyn${ComponentName} {}`; const index = elementTsText.indexOf('export') - 1;
const importString = `import { Lyn${ComponentName} } from './${componentname}'`; elementTsText =
elementTsText.slice(0, index) +
importString +
'\n' +
elementTsText.slice(index); fileSave(elementTsPath).write(elementTsText, 'utf8').end('\n'); // 新建刚才声明的所有文件
Files.forEach(file => {
fileSave(path.join(PackagePath, file.filename))
.write(file.content, 'utf8')
.end('\n');
}); // 将新组建添加到 nav.config.json
const navConfigFile = require('../../examples/nav.config.json'); Object.keys(navConfigFile).forEach(lang => {
const groups = navConfigFile[lang].find(item => Array.isArray(item.groups))
.groups;
groups[groups.length - 1].list.push({
path: `/${componentname}`,
title:
lang === 'zh-CN' && componentname !== chineseName
? `${ComponentName} ${chineseName}`
: ComponentName
});
}); fileSave(path.join(__dirname, '../../examples/nav.config.json'))
.write(JSON.stringify(navConfigFile, null, ' '), 'utf8')
.end('\n'); console.log('DONE!');

Makefile

Makefile 中增加如下配置:

new-lyn-ui:
node build/bin/new-lyn-ui.js $(filter-out $@,$(MAKECMDGOALS)) && npm run build:file help:
@echo " \033[35mmake new-lyn-ui <component-name> [中文名]\033[0m\t--- 创建新的 LynUI 组件 package. 例如 'make new-lyn-ui city 城市选择'"

icon 图标

element-ui 虽然提供了大量的 icon,但往往不能满足团队的业务需求,所有就需要往组件库中增加业务 icon,这里以 Iconfont 为例。不建议直接使用设计给的图片或者 svg,太占资源了。

  • 打开 iconfont

  • 登陆 -> 资源管理 -> 我的项目 -> 新建项目

    注意,这里为 icon 设置前缀时不要使用 el-icon-,避免和 element-ui 中的 icon 重复。这个项目就作为团队项目使用了,以后团队所有的业务图标都上传到该项目,所以最好注册一个团队账号。

  • 新建成功后,点击 上传图标至项目 ,选择 上传图标 ,上传设计给的 svg(必须是 svg),根据需要选择 保留颜色或不保留并提交

  • 上传完毕,编辑、检查没问题后,点击 下载至本地

  • 复制其中的 iconfont.ttficonfont.woff/packages/theme-lyn/src/fonts 目录下

  • 新建 /packages/theme-lyn/src/icon.less 文件,并添加如下内容

    @font-face {
    font-family: 'iconfont';
    src: url('./fonts/iconfont.woff') format('woff'), url('./fonts/iconfont.ttf') format('truetype');
    font-weight: normal;
    font-display: auto;
    font-style: normal;
    } [class^="lyn-icon-"], [class*=" lyn-icon-"] {
    font-family: 'iconfont' !important;
    font-style: normal;
    font-weight: normal;
    font-variant: normal;
    text-transform: none;
    line-height: 1;
    vertical-align: baseline;
    display: inline-block; /* Better Font Rendering =========== */
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale
    } /**
    * 示例:
    * .lyn-icon-iconName:before {
    * content: "\unicode 16进制码"
    * }
    * .lyn-icon-add:before {
    * content: "\e606"
    * }
    */
  • 执行 npm run gen-cssfile

  • 更新 /build/bin/iconInit.js 文件为以下内容

    'use strict';
    
    var postcss = require('postcss');
    var fs = require('fs');
    var path = require('path'); /**
    * 从指定的 icon 样式文件(entry)中按照给定正则表达式(regExp)解析出 icon 名称,然后输出到指定位置(output)
    * @param {*} entry 被解析的文件相对于当前文件的路径,比如:../../packages/theme-chalk/icon.css
    * @param {*} regExp 被解析的正则表达式,比如:/\.el-icon-([^:]+):before/
    * @param {*} output 解析后的资源输出到相对于当前文件的指定位置,比如:../../examples/icon.json
    */
    function parseIconName(entry, regExp, output) {
    // 读取样式文件
    var fontFile = fs.readFileSync(path.resolve(__dirname, entry), 'utf8');
    // 将样式内容解析为样式节点
    var nodes = postcss.parse(fontFile).nodes;
    var classList = []; // 遍历样式节点
    nodes.forEach((node) => {
    // 从样式选择器中根据给定匹配规则匹配出 icon 名称
    var selector = node.selector || '';
    var reg = new RegExp(regExp);
    var arr = selector.match(reg); // 将匹配到的 icon 名称放入 classList
    if (arr && arr[1]) {
    classList.push(arr[1]);
    }
    }); classList.reverse(); // 希望按 css 文件顺序倒序排列 // 将 icon 名称数组输出到指定 json 文件中
    fs.writeFile(path.resolve(__dirname, output), JSON.stringify(classList), () => { });
    } // 根据 icon.css 文件生成所有的 icon 图标名
    parseIconName('../../packages/theme-chalk/icon.css', /\.el-icon-([^:]+):before/, '../../examples/icon.json') // 根据 icon.less 文件生成所有的 sts icon 图标名
    parseIconName('../../packages/theme-lyn/src/icon.less', /\.lyn-icon-([^:]+):before/, '../../examples/lyn-icon.json')
  • 执行 npm run build:file,会看到在 /examples 目录下生成了一个 lyn-icon.json 文件

  • /examples/entry.js 中增加如下内容

    import lynIcon from './lyn-icon.json';
    Vue.prototype.$lynIcon = lynIcon; // StsIcon 列表页用
  • /examples/nav.config.json 中业务配置部分增加 lyn-icon 路由配置

    {
    "groupName": "LynUI",
    "list": [
    {
    "path": "/lyn-icon",
    "title": "icon 图标"
    }
    ]
    }
  • 增加文档 /examples/docs/{语言}/lyn-icon.md,添加如下内容

  • 查看官网 看图标是否生效

  • 后续如需扩展新的 icon

    • 在前面新建的 iconfont 项目中上传新的图标,然后点击 下载至本地,将其中的 iconfont.ttficonfont.woff 复制 /packages/theme-lyn/src/fonts 目录下即可(替换已有的文件)

    • /packages/theme-lyn/src/icon.less 中设置新的 icon 样式声明

    • 执行 npm run build:file

    • 查看官网 看图标添加是否成功

升级 Vue 版本

element-ui 本身依赖的是 vue@^2.5.x,该版本的 vue 不支持最新的 v-slot 插槽语法(v-slot 是在 2.6.0 中新增的),组件的 markdown 文档中使用 v-slot 语法不生效且会报错,所以需要升级 vue 版本。涉及三个包:vue@2.6.12、@vue/component-compiler-utils@3.2.0、vue-template-compiler@^2.6.12。执行以下命令即可完成更新:

  • 删除旧包

    npm uninstall vue @vue/component-compiler-utils vue-template-compiler -D
  • 安装新包

    npm install vue@^2.6.12 @vue/component-compiler-utils@^3.2.0 vue-template-compiler@^2.6.12 -D
  • 更新 package.json 中的 peerDependencies

    {
    "peerDependencies": {
    "vue": "^2.6.12"
    }
    }

扩展

到这里,组件库的架构调整其实已经完成了,接下来只需组织团队成员对照设计稿进行组件开发就可以了。但是对于一些有洁癖的开发者来说,其实还差点。

比如:

  • 团队的组件库不想叫 element-ui,有自己的名称,甚至整个组件库的代码都不想出现 element 字样

  • element-ui 的某些功能团队不需要,比如:官网项目(examples)中的主题、资源模块、chrome 插件(extension)、国际化相关(只保留中文即可)

  • 静态资源,element-ui 将所有的静态资源都上传到自己的 CDN 上了,我们去访问其实优点慢,可以将相关资源挪到团队自己的 CDN 上

  • 工程代码质量问题,element-ui 本身提供了 eslint,做了一点代码质量的控制,但是做的不够,比如格式限制、自动格式化等,可以参考 搭建自己的 typescript 项目 + 开发自己的脚手架工具 ts-cli 中的 代码质量 部分去配置

  • 替换官网 logo、渲染信息等

  • element-ui 样式库的优化,其实 element-ui 的样式存在重复加载的问题

    虽然它通过 webpack 打包已经解决了一部分问题,但是某些情况还是会出现重复加载,比如 table 组件中使用 checkbox 组件,就会加载两次 checkbox 组件的样式代码。有精力的同学可以去研究研究

  • 你的业务只需要 element-ui 的部分基础组件,把不需要的删掉,可以降低组件库的体积,提升加载速度

  • ...

这些工作有一些是对官网项目(examples)的裁剪,有一些是项目整体优化,还有一些是洁癖,不过,相信凡是进行到这一步的同学,都已经为团队构建出了自己的组件库,解决以上列出的那些问题完全不再话下,这里就不一一列出方法了。

链接

  • Element 源码架构 思维导图版
  • 组件库专栏
    • 如何快速为团队打造自己的组件库(上)—— Element 源码架构
    • Element 源码架构 视频版,关注微信公众号,回复: "Element 源码架构视频版" 获取

文章已收录到 github,欢迎 Watch 和 Star。

如何快速为团队打造自己的组件库(下)—— 基于 element-ui 为团队打造自己的组件库的更多相关文章

  1. vue+element ui 的上传文件使用组件

    前言:工作中用到 vue+element ui 的前端框架,使用到上传文件,则想着封装为组件,达到复用,可扩展.转载请注明出处:https://www.cnblogs.com/yuxiaole/p/9 ...

  2. 打造静态分析器(二)基于Asp.Net Core 3.0的AspectCore组件检测

    上一篇,我们打造了一个简单的分析器,但是我们实际使用分析器就是为了对项目做分析检测,增加一些非语法的自检的 比如Asp.Net Core 3.0的替换依赖注入检测 设计分析 我们创建一个默认的Asp. ...

  3. 基于element ui的级联选择器组件实现的分类后台接口

    今天在做资产管理系统的时候遇到一个分类的级联选择器,前端是用的element的组件,需要后台提供接口支持.     这个组件需要传入的数据结构大概是这样的,详细的可参考官方案例: [{ value: ...

  4. Vue + Element UI 实现权限管理系统 (功能组件封装)

    组件封装 为了避免组件代码的臃肿,这里对主要的功能部件进行封装,保证代码的模块化和简洁度. 组件结构 组件封装重构后,试图组件结构如下图所示 代码一览 Home组件被简化,包含导航.头部和主内容三个组 ...

  5. Vue+element UI实现表格数据导出Excel组件

    介绍 这是一个可以将页面中的表格数据导出为Excel文件的功能组件,该组件一般与表格一起使用,将表格数据传给组件,然后通过点击组件按钮可将表格中的数据导出成Excel文件. 使用方法 由于封装该组件内 ...

  6. krry-transfer ⏤ 基于 element 的升级版穿梭框组件发布到 npm 啦

    博客地址:https://ainyi.com/81 基于 element ui 的==升级版穿梭框组件==发布到 npm 啦 看过我之前博客的同学或许知道我之前写过关于 element 穿梭框组件重构 ...

  7. 基于VueJS的render渲染函数结合自定义组件打造一款非常强大的IView 的Table

    基于VueJS的render渲染函数结合自定义组件打造一款非常强大的IView 的Table https://segmentfault.com/a/1190000015970367

  8. 【原创】强撸基于 .NET 的 Redis Cluster 集群访问组件

    Hello 大家好,我是TANZAME,我们又见面了.今天我们来聊聊怎么手撸一个 Redis Cluster 集群客户端,纯手工有干货,您细品. 随着业务增长,线上环境的QPS暴增,自然而然将当前的单 ...

  9. 循序渐进VUE+Element 前端应用开发(20)--- 使用组件封装简化界面代码

    VUE+Element 前端应用,比较不错的一点就是界面组件化,我们可以根据重用的指导方针,把界面内容拆分为各个不同的组合,每一个模块可以是一个组件,也可以是多个组件的综合体,而且这一个过程非常方便. ...

  10. vue 快速入门 系列 —— vue 的基础应用(下)

    其他章节请看: vue 快速入门 系列 vue 的基础应用(下) 上篇聚焦于基础知识的介绍:本篇聚焦于基础知识的应用. 递归组件 组件是可以在它们自己的模板中调用自身的.不过它们只能通过 name 选 ...

随机推荐

  1. solr -window 安装与启动

    1.下载 官网路径   https://solr.apache.org/downloads.html 为了稳定,我用 5.4.1 版本的 ,   这是下载地址 https://archive.apac ...

  2. vue3.0+ts+setup语法糖props写法

    写法一 import defaultImg from '@/assets/images/defaultImg.png' const props = defineProps({ src: { type: ...

  3. Java 内幕新闻第二期深度解读

    这是由 Java 官方发布,Oracle JDK 研发 Nipafx 制作的节目,包含 JDK 近期的研发进展和新特性展望和使用,这里加上个人译制的字幕搬运而来.我把 Nipafx 的扩展资料详细研读 ...

  4. day 17 i++优先级大于 *i

    (1).有下列定义语句,int *p[4];以下选项中与此语句等价的是[C] (A).int p[4]; (B).int **P; (C).int *(p[4]); (D).int (*p)[4]; ...

  5. Go环境配置和GoModule

    Linux相关 Linux常用操作 mkdir directory --创建文件夹 vi file --创建文件,再关闭vim rm file --删除文件 rm -rf directory --递归 ...

  6. 【刷题-LeetCode】123 Best Time to Buy and Sell Stock III

    Best Time to Buy and Sell Stock III Say you have an array for which the ith element is the price of ...

  7. 🏆【Alibaba中间件技术系列】「Nacos技术专题」配置中心加载原理和配置实时更新原理分析(上)

    官方资源 https://nacos.io/zh-cn/docs/quick-start.html Nacos之配置中心 动态配置管理是 Nacos的三大功能之一,通过动态配置服务,可以在所有环境中以 ...

  8. POSIX之消息队列

    my_semqueue_send.c: #include<stdio.h> #include<errno.h> #include<mqueue.h> #includ ...

  9. golang中的匿名函数三种用法

    package main import ( "fmt" "strconv" ) func main() { // 匿名函数的使用:方式1 f1 := func( ...

  10. cp 不提示按y

    yes|cp index.html.j2   yml -rf \cp index.html.j2   yml/ 两个效果是一样的