從零開始搭建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/indeximport Col from ./col/indexconst 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 參考: w3cplus.com/css/css-arc

說到這裡,目前對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/indeximport ../packages/theme-default/lib/index.cssVue.use(VVUI)

一切顯得那麼美好....

優化與不足

  • 組件導出代碼暫不支持自動化生成:比如我們的組件index文件,每次添加組件都需要不斷地改寫,我們2*

    可以嘗試進行webpack配置,npm run dev 的時候自動進行組件檢測,然後幫我們寫好導出代碼。
  • 目錄結構劃分缺陷:目前所有內容僅支持中文,如果想要做到支持國際化,那麼還需要重新調整目錄結構。
  • 發布tag: 需要編寫腳本支持tag發布
  • 組件太少:文檔剛寫,組件還不是很多,慢慢去維護,相信會越來越多的組件,做業務的過程中也可以把常用的組件加進去,這樣更加方便自己以後的維護和學習

結語:

項目github地址:github

項目演示地址: 演示

歡迎 PR 一起維護,歡迎 Star

關於

作者:monkeyWang

本人主頁:monkeyWang

微信公眾號:會不定期推送前端技術文章,歡迎關注

weixin.qq.com/r/YiixqYj (二維碼自動識別)


推薦閱讀:

想要對 HTML 和 CSS 有深入的理解,是不是需要學習傳統排版的知識?如果需要,應該學習到什麼程度?
可以用JQuery 調用Python對象的function嘛?
HTML大面積使用ID是對是錯?

TAG:Vuejs | 前端开发 | HTML |