前段時間一直在研究Vue組件庫,終於在組內派上了用場。來給大家貢獻一篇關於Vue組件庫的相關知識。經驗不多,如果有不合理的地方還請多多指出哦~~~
回想一下,在你們公司或者你們小組是否有一個以上的項目需要你維護?你是否遇到兩個項目需要開發類似的功能的情況?那么你是怎么做的呢?
有這么三種常用的解決方案:
-
COPY 你可能會說我講究速度,復制之前的組件到新項目中,慢慢的你會發現隨着你的項目的增加代碼量在成倍上升,重復工作浪費了你很多時間。
-
子模塊 我可以抽離出所有公共的組件放入一個子模塊(git submodule)中,這種方式雖然解決了重復工作,但對目錄結構以及使用者的要求比較多,不夠靈活,還是不滿意。。。
-
使用開源組件庫 這可能是一個好的選擇,但是,一用才發現很多並不是我們想要的,尤其是移動端組件庫:
-
C端產品定制化需求多
-
組件庫風格與產品不符
適配方案不同rem/px/vw等。
針對以上痛點,寫一個通用組件庫是較優的方案
內容分為兩部分
-
組件庫的兩個核心思想的實現:全局引用和按需引用。
-
從使用者和開發者兩個角度看問題
我們以一個簡單的vue組件庫為例講一下重點部分:
全局引用
1.把公共組件放入components目錄中,並編寫導出文件如下:
// src/index.js import Btn from './components/btn' import Swipe from './components/swipe' const install = function(Vue) { if (install.installed) return; // 此處注意:組件中需要添加name屬性,代表注冊的組件名,也可修改成其他 Vue.component(Btn.name, Btn) Vue.component(Swipe.name, Swipe) } // Vue 是全局變量時,自動 install if (typeof window !== 'undefined' && window.Vue) { install(window.Vue); }; module.exports = { install, Btn, Swipe }
此處需要注意的是 install。 Vue的插件必須提供一個公開方法 install,該方法會在你使用該插件,也就是 Vue.use(yourPlugin)時被調用。這樣也就給 Vue全局注入了你的所有的組件。
2.webpack配置
var path = require('path') var options = require('./webpack.base') var merge = require('webpack-merge') var webpack = require('webpack') var ExtractTextPlugin = require('extract-text-webpack-plugin') module.exports = merge(options, { entry: path.resolve(__dirname, '../src/index.js'), output: { filename: 'UiLib.js', path: path.resolve(__dirname, '../dist'), library: 'UiLib', libraryTarget: 'umd' // commonjs2 }, externals: { vue: { root: 'Vue', commonjs: 'vue', commonjs2: 'vue', amd: 'vue' } }, plugins: [ new ExtractTextPlugin({ filename: './style.css', disable: false, allChunks: true }) ] })
這里需要注意:必須加上externals(產出文件不打包vue)和library,libraryTarget(打包產出模式)。可以去webpack官網查看詳細說明。樣式需要單獨打包出來,否則打包的js文件過大影響性能。
3.在package.json中添加:
{ "main": "dist/UiLib.js", // 引用的入口文件 "name": "vue-ui-lib", "version": "0.0.1" }
到此一個簡單的組件庫就可以publish了,趕快試試吧。那發布后如何使用呢
4.引用
npm i vue-ui-lib [--register] [服務器地址]
// index.js import UiLib from 'vue-ui-lib' Vue.use(UiLib)
// test.vue 組件名即上邊提到的組件中的name屬性,由自己定義
<btn ...></btn>
<swipe ...></swipe>
這樣雖然通了,可是我要修改組件,測試組件效果怎么辦?很明顯,需要一個demo做測試。
5.創建demo,並在demo的入口文件中引入
// demo/index.js import UiLib from '../src/index' Vue.use(UiLib)
然后按照類似你的項目中的webpack配置一樣配置好,你的測試也就能跑通了。可是,你又發現有些項目只用一個組件,但Vue.use后會把所有的組件引入。 所以如何做到按需引入組件呢?首先想到的就是webpack的多入口文件啦。
按需引用
6.按需引入
module.exports = merge(options, { // 舉例 entry: { 'btn': path.resolve(__dirname, '../src/components/btn/index.js'), 'swipe': path.resolve(__dirname, '../src/components/swipe/index.js') }, output: { filename: '[name]/index.js', path: path.resolve(__dirname, '../dist'), library: 'ypUiLib', libraryTarget: 'umd' } })
這樣產出后在dist目錄下就會出現btn, swipe目錄。


那么按需引入怎么引入呢?
import Btn from 'vue-ui-lib/dist/btn'
Vue.component(Btn.name, Btn)
或者
components: {
Btn
}
// 但是很多使用過組件庫的人會有疑問,應該是這樣的吧?
import {Btn} from 'vue-ui-lib'
第二種引用方式引用的是model.exports的導出文件,是全部引入的(親測是全部引入)。針對這個問題ant-design提供了一種解決方案:引入npm包 babel-plugin-import,這個包的原理是修改babel解析過程,配置到項目中后就會解析成第一種形式的代碼。
按需引用的css可以分開打包,但這樣會造成使用者的麻煩,所以直接引入所有的css可能是最好的選擇。當然如果樣式比較少的話,也可以直接打包到js中。
前面主要講了組件庫的兩個核心思想的實現:全局引用和按需引用。接下來從兩個角度出發說一些注意事項:
從使用者角度出發
-
css管理
使用者會說修改組件庫的樣式怎么這么難呢?一看發現是組件中用了css modules或者層級嵌套太深,或者是在vue的style中寫了scoped。 可是去掉這些又會導致全局樣式出現,萬一跟用戶定義的class重名怎么辦,所以我們就需要class命名規范,可以用postcss的@component-namespace name {...}統一管理。
// bad 使用者不好修改組件樣式
<style lang="scss" scoped>
.loading {
color: red;
.box {
width: pxToRem(60px);
}
}
...
</style>
// good 這樣命名都加了前綴,並且沒有層級嵌套
<style lang="scss">
//統一如下zzyp-[組件名]-[other]...
.zzyp-loading {
position: fixed;
top: 0;
&-box {
width: pxToRem(60px);
}
...
}
</style>
-
版本控制
-
依賴包的版本 為了讓使用者安裝的時候不會因為版本不一樣出現一些不可預料的錯誤,我們需要package lock,可以使用npm 5的lock,也可以使用yarn管理
-
自身版本 控制好自身版本,一般版本號是
x.y.z,盡量避免手動修改package.json。可以用npm version<update_type>自動生成。update_type三選一patch/minor/major分別對應補丁,新特性,很大的改動。
-
說明文檔
-
demo和文檔最好放到一起,示例越多越好
-
屬性,方法,slot等必須都寫清楚含義默認值等
從開發者角度出發
-
js提取所有公共模塊,工具函數等
-
為了提高開發效率,可以寫一些自動生成文件,比如src/index.js等
-
開發文檔需要寫清楚,並且寫清楚需要遵守的規范
現在組件庫的基本功能完成啦。其實這只是最基礎的部分,還有很多性能,組件內部如何設計等都沒有講到。如果要做一個復雜的通用組件庫要考慮的還有很多很多,可以去看看各位大神們是如何設計的,我就不誤導大家啦,吼吼~~~
如果你喜歡我們的文章,關注我們的公眾號和我們互動吧。

