• 从零开始搭建Vue组件库 VV-UI


    前言:

    前端组件化是当今热议的话题之一,也是我们在开发单页应用经常会碰到的一个问题,现在我们有了功能非常完善的Element-UI。各个大厂也相继宣布开源XXX-UI。但是也会存在一些问题,比如每个公司可能需要的业务组件不尽相同,或者我们想自己开发一套属于自己的组件库,来增强对组件的可控性。那么我们该如何去做呢?
    这里记录一下我从零开始搭建起来的组件库的过程,目前只有简单几个组件,不过我也会慢慢更新维护。

    项目github地址:github
    项目演示地址: 演示

    1. 环境准备

    我们搭建组件库,需要准备一系列环境,首先我们要考虑一下问题:

    1. 脚手架如何搭建
    2. 如何规划目录结构
    3. 如何编写文档

    首先,对于脚手架环境的问题,目前已经有非常成熟的vue官方的脚手架,我们拿来用就好了

    # 全局安装 vue-cli
    $ npm install --global vue-cli
    # 创建一个基于 webpack 模板的新项目
    $ vue init webpack my-project
    # 安装依赖,走你
    $ cd my-project
    $ npm install
    $ npm run dev
    

      

    接着我们看第二个问题,如何规划好我们组建的目录结构?首先我们需要有一个目录存放组件,有一个目录存放示例。所以我们要对vue-cli 生成的项目结构做一下改造:

    .
    ...
    |-- examples      // 原 src 目录,改成 examples 用作示例展示
    |-- packages      // 新增 packages 用于编写存放组件
    ...
    . 
    

      

    这样的话 我们需要再把我们webpack配置文件稍作一下调整,首先是把原先的编译指向src的目录改成examples,其次为了 npm run build 能正常编译 packages 我们也需要为 babel-loader 再增加一个编译目录:

    {
       test: /.js$/,
       loader: 'babel-loader',
       include: [resolve('examples'), resolve('test'), resolve('packages')]
    }
    

      

    这样我们搭建起来一个简易的目录结构。
    紧接着我们需要考虑如何编写文档。对于文档的编写,自然是markdown最合适不过了,那么怎么让我们在vue下可以去写 markdown 文档呢?答案当然是 vue-markdown-loader。然后我们按照文档配置了相关的插件信息:

    rules: [
       {
         test: /.md$/,
         loader: 'vue-markdown-loader'
       }
     ]

    好了,我们可以开始尝试写文档了,在 example/docs 目录下新建 test.md。

    # test
    > Hello World
    

    同时创建一个新的路由,指向我们的md文件:

    {
      path: '/test',
      name: 'test',
      component: r => require.ensure([], () => r(require('../docs/test.md')))
    }
    

      

    打开我们的浏览器http://localhost:8080/#/test 哈哈 真的成功了。别高兴的太早.... 问题还在后面:我们期望的文档不仅能编译markdown,而且最好能识别demo代码块一方面做演示,一方面可以显示演示代码最好了,就像这样:

    那我们需要怎么做呢?vue-mark-down 功能肯定不止这些!于是我们继续阅读它的文档,发现其实他就是封装了 markdown-it,支持 options 选项。这样我们就可以为我们的markdown定义独特的标识符,这里我用 demo 标识需要显示代码块的地方,所以我需要配置options 选项 :

    const vueMarkdown = {
      preprocess: (MarkdownIt, source) => {
        MarkdownIt.renderer.rules.table_open = function () {
          return '<table class="table">'
        }
        MarkdownIt.renderer.rules.fence = utils.wrapCustomClass(MarkdownIt.renderer.rules.fence)
        return source
      },
      use: [
        [MarkdownItContainer, 'demo', {
          // 用于校验包含demo的代码块
          validate: params => params.trim().match(/^demos*(.*)$/),
          render: function(tokens, idx) {
            
            var m = tokens[idx].info.trim().match(/^demos*(.*)$/);
    
            if (tokens[idx].nesting === 1) {
              var desc = tokens[idx + 2].content;
              // 编译成html
              const html = utils.convertHtml(striptags(tokens[idx + 1].content, 'script'))
              // 移除描述,防止被添加到代码块
              tokens[idx + 2].children = [];
    
              return `<demo-block>
                            <div slot="desc">${html}</div>
                            <div slot="highlight">`;
            }
            return '</div></demo-block>
    ';
          }
        }]
      ]
    }
    

      

    这里简单的描述一下这段代码是干什么的:首先把内容里面vue片段编译成html,用于显示,另一方面用highlight来高亮代码块。demo-block本身是我们定义好的组件:

    <template>
      <div class="docs-demo-wrapper">
          <div :style="{maxHeight: isExpand ? '700px' : '0'}" class="demo-container">
            <div span="14">
              <div class="docs-demo docs-demo--expand">
                <div class="highlight-wrapper">
                  <slot name="highlight"></slot>
                </div>
              </div>
            </div>
          </div>
        <span 
               class="docs-trans docs-demo__triangle" 
               @click="toggle">{{isExpand ? '隐藏代码' : '显示代码'}}</span>
      </div>
    </template>
    

      

    这样,我们的 test.md 便可以这么去写了:

    2. 如何编写组件

    环境准备完毕,紧接着要开始编写组件,考虑的是组件库,所以我们竟可能让我们的组件支持全局引入和按需引入,如果全局引入,那么所有的组件需要要注册到Vue component 上,并导出:

    const install = function(Vue) {
      if (install.installed) return;
      components.map(component => Vue.component(component.name, component));
    };
    
    export default {
      install
    };
    

      

    接着要实现按需加载,我们只需要单个导出组件即可:

    import Button from './button/index.js';
    import Row from './row/index'
    import Col from './col/index'
    
    const components = [
      Button,
      Row,
      Col
    ];
    
    const install = function(Vue) {
      if (install.installed) return;
      components.map(component => Vue.component(component.name, component));
    };
    
    if (typeof window !== 'undefined' && window.Vue) {
      install(window.Vue);
    }
    
    export default {
      install,
      Button,
      Row,
      Col
    };

    其次,我们还需要考虑一个问题:既然是单页面应用,必然要去解决样式冲突问题,如果组件内使用soped,那么样式就无法从组件内抽离出来,达不到可定制化主题颜色的目的。我们需要一套可以分离处理的样式,可以自行编译,可以相互不污染。这时候css 的BEM规范就显得尤为重要。如果你还不知道什么是BEM 参考: 
    说到这里,目前对BEM规范支持较好的插件就是postcss了,他允许我们配置BEM之间的连接符和缩写:

    {
      "browsers": ["ie > 8", "last 2 versions"],
      "features": {
        "bem": {
          "shortcuts": {
            "component": "b",
            "modifier": "m",
            "descendent": "e"
          },
          "separators": {
            "descendent": "__",
            "modifier": "--"
          }
        }
      }
    }
    

      

    这样我们就可以把样式单独的抽离出来,通过gulp进行打包编译:

    gulp.task('compile', function() {
      return gulp.src('./src/*.css')
        .pipe(postcss([salad]))
        .pipe(cssmin())
        .pipe(gulp.dest('./lib'));
    });
    

      

    最后生成我们的样式代码。

    好了开始我们的测试:

    import VVUI from '../packages/index'
    import '../packages/theme-default/lib/index.css'
    
    Vue.use(VVUI)
    

      

    一切显得那么美好....

    优化与不足

    • 组件导出代码暂不支持自动化生成:比如我们的组件index文件,每次添加组件都需要不断地改写,我们2*
      可以尝试进行webpack配置,npm run dev 的时候自动进行组件检测,然后帮我们写好导出代码。
    • 目录结构划分缺陷:目前所有内容仅支持中文,如果想要做到支持国际化,那么还需要重新调整目录结构。
    • 发布tag: 需要编写脚本支持tag发布
    • 组件太少:文档刚写,组件还不是很多,慢慢去维护,相信会越来越多的组件,做业务的过程中也可以把常用的组件加进去,这样更加方便自己以后的维护和学习

    结语:

    项目github地址:github
    项目演示地址: 演示
    欢迎 PR 一起维护,欢迎 Star

    关于

    作者:monkeyWang

    本人主页:monkeyWang

    微信公众号:前端知识铺

  • 相关阅读:
    greenDao 学习之坑 "java-gen" 目录下的类不能引用
    fastboot 刷system.img 提示 sending 'system' (*KB)... FAILED (remote: data too large)
    AndroidStudio导入第三方开源库 --文件夹源码
    git 克隆项目 与 分支简单操作
    Jquery当选中后费用或什么信息会自增长
    Jquery中各种标签的含义集合
    Jquery判断是否重复(连接到后台数据库进行比较)
    Jquery按空格键选中复选框或单选框
    JS(JQuery)操作Array的相关方法
    前端知识点-人资相关知识点
  • 原文地址:https://www.cnblogs.com/tiedaweishao/p/7825997.html
Copyright © 2020-2023  润新知