從零開始搭建Vue組件庫 VV-UI

前言:

前端組件化是當今熱議的話題之一,也是咱們在開發單頁應用常常會碰到的一個問題,如今咱們有了功能很是完善的Element-UI。各個大廠也相繼宣佈開源XXX-UI。可是也會存在一些問題,好比每一個公司可能須要的業務組件不盡相同,或者咱們想本身開發一套屬於本身的組件庫,來加強對組件的可控性。那麼咱們該如何去作呢?
這裏記錄一下我從零開始搭建起來的組件庫的過程,目前只有簡單幾個組件,不過我也會慢慢更新維護。javascript

項目github地址:github
項目演示地址: 演示css

1. 環境準備

咱們搭建組件庫,須要準備一系列環境,首先咱們要考慮一下問題:html

  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 生成的項目結構作一下改造:vue

.
...
|-- examples      // 原 src 目錄,改爲 examples 用做示例展現
|-- packages      // 新增 packages 用於編寫存放組件
...
. 

  

這樣的話 咱們須要再把咱們webpack配置文件稍做一下調整,首先是把原先的編譯指向src的目錄改爲examples,其次爲了 npm run build 能正常編譯 packages 咱們也須要爲 babel-loader 再增長一個編譯目錄:java

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

  

這樣咱們搭建起來一個簡易的目錄結構。
緊接着咱們須要考慮如何編寫文檔。對於文檔的編寫,天然是markdown最合適不過了,那麼怎麼讓咱們在vue下能夠去寫 markdown 文檔呢?答案固然是 vue-markdown-loader。而後咱們按照文檔配置了相關的插件信息:webpack

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

 

好了,咱們能夠開始嘗試寫文檔了,在 example/docs 目錄下新建 test.md。git

# test
> Hello World

同時建立一個新的路由,指向咱們的md文件:github

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

  

打開咱們的瀏覽器http://localhost:8080/#/test 哈哈 真的成功了。別高興的太早.... 問題還在後面:咱們指望的文檔不只能編譯markdown,並且最好能識別demo代碼塊一方面作演示,一方面能夠顯示演示代碼最好了,就像這樣:web

那咱們須要怎麼作呢?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 參考: 
說到這裏,目前對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

微信公衆號:前端知識鋪

相關文章
相關標籤/搜索