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

前言:

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

1. 环境准备

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

  1. 脚手架如何搭建html

  2. 如何规划目录结构前端

  3. 如何编写文档vue

首先,对于脚手架环境的问题,目前已经有很是成熟的vue官方的脚手架,咱们拿来用就行了java

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

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

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

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

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

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

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

好了,咱们能够开始尝试写文档了,在 example/docs 目录下新建 test.md。web

# 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(/^demo\s*(.*)$/),
      render: function(tokens, idx) {
        
        var m = tokens[idx].info.trim().match(/^demo\s*(.*)$/);

        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>\n';
      }
    }]
  ]
}

这里简单的描述一下这段代码是干什么的:首先把内容里面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 参考: http://www.w3cplus.com/css/css-architecture-1.html
说到这里,目前对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

微信公众号:会不按期推送前端技术文章,欢迎关注

image.png

相关文章
相关标签/搜索