最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 如何快速为团队打造自己的组件库(下)—— 基于 element-ui 为团队打造自己的组件库

    正文概述 掘金(李永宁)   2021-03-09   1335

    链接

    如何快速为团队打造自己的组件库(上)—— Element 源码架构

    主题配置

    基础组件库在 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-ui 为团队打造自己的组件库

      原因是 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;

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

    • 编译主题

      修改完以后,保存文件,然后执行以下命令编译主题,会产生一个 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,不要删掉,后面需要用

      • 修改 /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 为团队打造自己的组件库

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

    样式深度定制

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

    样式目录

    • 主题配置 步骤中备份的 /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
        
      • /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 组件

    • 重新生成主题,然后覆盖现在的 /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

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

      如何快速为团队打造自己的组件库(下)—— 基于 element-ui 为团队打造自己的组件库
    • 新建成功后,点击 上传图标至项目 ,选择 上传图标 ,上传设计给的 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,添加如下内容

      如何快速为团队打造自己的组件库(下)—— 基于 element-ui 为团队打造自己的组件库
    • 查看官网 看图标是否生效

      如何快速为团队打造自己的组件库(下)—— 基于 element-ui 为团队打造自己的组件库
    • 后续如需扩展新的 icon

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

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

      • 执行 npm run build:file

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

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

    升级 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 的样式存在重复加载的问题

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

    • ...

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

    链接

    如何快速为团队打造自己的组件库(上)—— Element 源码架构


    起源地下载网 » 如何快速为团队打造自己的组件库(下)—— 基于 element-ui 为团队打造自己的组件库

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元