基於 vue-cli3 打造屬於自己的 UI 庫
小小的前端有大大的夢想。做一個屬於我們自己的 UI 元件庫應該是個不錯的小目標了。那。。。元件是什麼:hushed:?我想應該不用多說,在日常開發中我們天天與之打交道,你所寫的每一個 vue 檔案都可以當做是一個元件,只不過通用性有所區別而已。元件寫得多了,隨著時間的推移,你就會有寫一個元件庫的衝動了。其實更多的為了提升 B 格:cry:,當和別人談論的時候,你說你寫過一個 UI 庫,別人就覺得你可能有點吊。好了,不吹 B,趕緊擼吧:stuck_out_tongue_winking_eye:
原始碼地址: github.com/lgq627628/x…
知識前置
我們可能習慣了在一個 vue 裡面引入元件的方式,所以先這裡要鞏固一下全域性引入元件的方式。舉個栗子:chestnut:,一般我們的用法是這樣的:
import Loading from '../components/loading' // 方法一:name 是元件的名字 Vue.component(Loading.name, Loading) // 方法二:前提是 Loading 有提供 install 這個方法 Vue.use(Loading); 複製程式碼
上面兩種方式都可以用來全域性註冊元件,但是引數不一樣,選擇哪種方法都可以,但要留意一下第二種方法,這種方法需要元件本身有個 install 的方法。額。。。為啥要有 install 的方法?這個你就當做是規定就好,你用人家的框架就得遵循人家的規則。扯犢子吧:ox:,其實是因為執行 Vue.use
的時候會執行裡面的 install 方法,所以我們需要寫個 install。對此你不用較真,寄人籬下是這樣的,哦不對:no_good:♀️,應該是站在大神的肩膀上:muscle:。
搭建目錄
快速建立專案
這步應該不用多說吧,執行一下 vue create xr-ui
(前提是你安裝了 vue-cli3),然後看自己喜好選擇一些配置,幾下回車之後一個清爽的初始專案就有了。這裡我沒有選擇用 typescript(主要是我還沒習慣用它),所以選用 typescript 的同學們注意啦,可能寫法會有少許不同,有勞大夥看著改下吧。
修改目錄結構
- 把 src 目錄名字改成 examples,這是用於展示元件示例的
- 在根目錄下新建一個 packages 資料夾,這是用來放元件的
當我們水平不夠的時候,模仿是一種強大的學習能力:clap:。
新增配置檔案
小改了一下目錄之後,你會驚奇的發現專案執行不了了。沒關係,這很正常,畢竟 src 都不見了,路徑啥的肯定得報錯。所以現在我們來解決這個問題。 在根目錄下新建一個 vue.config.js 檔案(新專案是沒有這個檔案的),並寫入以下內容:
const path = require('path') module.exports = { // 修改 pages 入口 pages: { index: { entry: 'examples/main.js', // 入口 template: 'public/index.html', // 模板 filename: 'index.html' // 輸出檔案 } }, // 擴充套件 webpack 配置 chainWebpack: config => { // @ 預設指向 src 目錄,這裡要改成 examples // 另外也可以新增一個 ~ 指向 packages config.resolve.alias .set('@', path.resolve('examples')) .set('~', path.resolve('packages')) // 把 packages 和 examples 加入編譯,因為新增的檔案預設是不被 webpack 處理的 config.module .rule('js') .include.add(/packages/).end() .include.add(/examples/).end() .use('babel') .loader('babel-loader') .tap(options => { // 修改它的選項... return options }) } } 複製程式碼
上面的註釋應該都寫的挺明瞭,主要就是修改別名、修改入口檔案以及把新檔案加入 webpack 編譯這幾個步驟。然後我們再執行一下程式就可以跑得通了。至於為什麼這麼配置、或者怎麼配置,不瞭解的同學可以去Vue Cli 官網看下,上面寫的是清清楚楚、明明白白,然而我也只是懂那麼一兩個配置而已:sob::sob::sob:,還沒學會 webpack 的套路,因為常常是用的時候看一眼,一陣子不用就又忘了,沒辦法 ♀️腦子不行。
編寫元件
一個元件庫沒有元件怎麼行呢,所以我們要先寫個 test 元件(你可以隨便寫,這不重要)。ok:ok_hand:,我們先在 packages 目錄下新建一個 test 資料夾,再在 test 資料夾下下面新建一個 src 資料夾,在 src 資料夾下面新建一個 test.vue 元件,大概長下面這樣子:point_down::

<!--test.vue--> <template> <div class="xr-test" @click="handleClick">{{ num }}</div> </template> <script> export default { name: 'XrTest', // 這個名字很重要,它就是未來的標籤名<xr-test></xr-test>,坑了我一下 data () { return { num: 0 } }, methods: { handleClick () { this.num++ } } } </script> <style lang="scss" scoped> .xr-test { width: 100px; height: 100px; line-height: 100px; border-radius: 50%; font-size: 30px; text-align: center; background: #24292e; color: white; } </style> 複製程式碼
應該都能看懂吧,不過多解釋。:warning:這裡主要強調一點,就是 name 這個名字尤為重要,我就在這個坑裡呆了挺久。首先它是必須要寫的,為啥呢,你可以把它理解為 id,具有唯一標識元件的作用,將來我們可是要通過這個 name 來找到和判定這是什麼元件,所以你寫的所有元件應該是不重名的;其次這個 name 就是我們最終的標籤名,比如這裡我們的 name 是 XrTest
,到時候我們寫的標籤就長這樣 <xr-test></xr-test>
,就像 Element 一樣,name 是 ElButton
,用的時候就是 <el-button></el-button>
。
暴露元件
讓我們在 packages/test 下面新建一個 index.js 檔案,具體程式碼如下:
// 為元件提供 install 方法,供元件對外按需引入 import XrTest from './src/test' XrTest.install = Vue => { Vue.component(XrTest.name, XrTest) } export default XrTest 複製程式碼
這步的精髓就在於給元件擴充套件一個 install 方法,至於為什麼要擴充套件這個方法,文章開頭已經說到了,是因為 Vue.use()
的需要,use 會預設呼叫 install 方法安裝,僅此而已。接著我們在 packages 下面也新建一個 index.js 檔案,注意和上面那個 index.js 區別開,上面那個是針對單個元件安裝的,這個是針對所有元件全域性安裝的,先看程式碼:
import XrTest from './test' // 所有元件列表 const components = [ XrTest ] // 定義 install 方法,接收 Vue 作為引數 const install = function (Vue) { // 判斷是否安裝,安裝過就不繼續往下執行 if (install.installed) return install.installed = true // 遍歷註冊所有元件 components.map(component => Vue.component(component.name, component)) // 下面這個寫法也可以 // components.map(component => Vue.use(component)) } // 檢測到 Vue 才執行,畢竟我們是基於 Vue 的 if (typeof window !== 'undefined' && window.Vue) { install(window.Vue) } export default { install, // 所有元件,必須具有 install,才能使用 Vue.use() ...components } 複製程式碼
這步的主要作用就是統一匯出所有元件及暴露 install 方法。之前的 index.js 只是安裝單個元件,而現在這個 index.js 是迴圈安裝所有元件,具體使用就看你是不是要按需引用了。這裡給個目錄結構方便大家觀看:

因為這步挺重要的,所以建議好好停下來理解消化一下 。。。
當然你可能會問道,為什麼這樣建目錄?還能什麼原因,因為 Element 是這樣(如下圖),所以我們這樣寫,僅此而已。

元件測試
ok,元件寫完了,接下來我們就在 examples 下面測試一下,看看能不能引用成功。 首先在 examples 下的 main.js 中引入剛剛寫好的包,就像下面這樣:

然後把 examples/views 下面的 Home.vue 裡面的內容刪了,寫入自己標籤元件,就像下面這樣:

yarn serve
,看看效果,嗯,還湊合吧。
庫模式打包
在 vue-cli3 中我們通過以下命令可以將一個單獨的入口打包成一個庫:
// target: 預設為構建應用,改為 lib 即可啟用構建庫模式 // name: 輸出檔名 // dest: 輸出目錄,預設為 dist,這裡我們改為 lib // entry: 入口檔案路徑 vue-cli-service build --target lib --name lib [entry] 複製程式碼
要注意的是在庫模式中,打包出來的庫中是不包含 Vue 的。 然後我們修改一下 package.json 檔案,就像下面這樣:

npm run lib
就能生成庫啦,看看左側的目錄是不是多了個 lib 資料夾,那個就是我們要釋出的東西。


補充下,lib 目錄下面的 js 之所以有好幾種,是因為有兩種規範(common 和 umd)、是否壓縮(min)和對映(map)的區別,暫且知道有這麼回事就行,不用深究。
釋出到npm
萬事俱備,只欠釋出。
- 完善一下 README.md 文件,這個隨便寫兩句就好
- 修改一下 package.json 檔案:
{ "name": "xr-ui", "version": "0.3.0", "description": "基於 vue-cli3 的 UI 元件庫", "main": "lib/xr-ui.umd.min.js",// 這是 lib 目錄下的其中一個 "keywords": "xr-ui", "private": false, "license": "MIT" } 複製程式碼
- 在根目錄下新建一個 .npmignore 檔案,內容和 .gitignore 差不多:
# 這是複製 .gitignore 裡面的 .DS_Store node_modules /dist # local env files .env.local .env.*.local # Log files npm-debug.log* yarn-debug.log* yarn-error.log* # Editor directories and files .idea .vscode *.suo *.ntvs* *.njsproj *.sln *.sw* # 以下是新增的 # 要忽略目錄和指定檔案 examples/ packages/ public/ vue.config.js babel.config.js *.map *.html 複製程式碼
最後執行 npm login
登入 npm 賬號,再執行 npm publish
釋出即可,就這麼簡單的兩步就可以,過一會在 npm 上就能搜到了。當然前提是你有個 npm 賬號,沒有的話去註冊一個吧,很 easy 的,然後還要搜下你的 npm 包名是否有人用,有的話就換一個。
小試牛刀
終於,歷盡千辛萬苦,我們可以引用自己寫的庫拉,想想就牛叉。別激動,讓我們試驗一下,用 vue create new
另起一個新專案,然後 npm i xr-ui -S
,可以在 node_modules 裡面看到我們的包大概長這樣:

然後在 main.js 引入:
import Vue from "vue" import XrUI from 'xr-ui' import 'xr-ui/lib/xr-ui.css' Vue.use(XrUI) 複製程式碼
這樣我們就能在頁面中引入元件啦,哈哈哈哈,賊開心,喜上眉梢。。。
<xr-test></xr-test> 複製程式碼
小結
想想如果你為團隊維護一個元件庫,那是多麼牛叉的事情。當然,好記性不如爛鍵盤,看懂並不代表掌握,只有自己體驗過才是真的難忘,:sweat:額。。怎麼突然抒情了起來。
最後還是強調一點吧,水平不夠的時候,模仿是一種必備的技能,就像我。不過沒關係,這只是個過程,當你寫了幾年的程式碼後,總會有一些自己的想法,然後也能慢慢沉澱出一些屬於自己的東西,我們只是站在巨人的肩膀上前行,僅此而已,回見:wave::wave::wave: