如何開發一個基於 Vue 的 ui 元件庫(一)
開發模式
預覽 demo
在開發一個 ui 元件庫時,肯定需要一邊預覽 demo,一邊修改程式碼。
常見的解決方案是像開發一般專案一樣使用 webpack-dev-server
預覽元件,比如通過 vue-cli
初始化專案,或者自己配置指令碼。
文藝一點兒地可能會用到 parcel
來簡化 demo 的開發配置(比如 muse-ui)。
展示文件
作為一個 ui 元件庫,也肯定要有自己的元件展示文件。
一般業界常見方案是自己開發展示文件...
但這樣會帶來一個元件庫和文件如何同步的問題。
為何不用 vuepress?
由於 vuepress 支援在 markdown 中插入元件
從開發步驟上來說,甚至可以先寫文件說明,再具體地編寫程式碼實現元件功能。這樣一來文件即是預覽 demo,與元件開發可以同步更新。
p.s. React 的元件文件可以試試這倆庫:
型別宣告
在開發和使用過程中如果對於一些物件、方法的引數能夠智慧提示,豈不美哉?
如何實現呢?
其實就是在相應資料夾中新增元件相關的型別宣告(*.d.ts
),並通過 src/index.d.ts
匯出。
{
"typings": "src/index.d.ts",
}
複製程式碼
一開始將宣告檔案都放在
types/
資料夾下,但在實踐中覺得還是放在當前資料夾下比較好。一方面有利於維護,另一方面是讀取原始碼時也有型別提示。
如何打包
打包工具
和打包庫一樣,選了 rollup。
單檔案元件
在開發中用不用 *.vue
這樣的單檔案元件來開發呢?
- muse-ui 完全不寫
<template>
只使用render
函式。 - iview、element、vant 使用
.vue
檔案,但樣式單獨寫。 - ant-design-vue 使用
.jsx
檔案,樣式也單獨寫。 - vux 使用帶
<style>
的.vue
檔案,但在使用時必須用 vux-loader - cube-ui 使用帶
<style>
的.vue
檔案,但有一些配置。
講道理,完全不寫 <template>
有點兒麻煩,所以添加了 rollup-plugin-vue 外掛用於打包 .vue
檔案。
但碰到一個問題:如何打包 <style>
中的樣式?
- 首先嚐試不寫
<style>
,直接在 js 裡 import scss 檔案。沒問題,但是寫元件時不直觀,同一組件的程式碼也分散在了兩個地方 - 接著嘗試配置 rollup-plugin-vue,碰到一個 source-map 報錯的問題。我提了個 issue。
載入方式
區分場景
為了區分不同的場景使用不同的 js,所以一共打包了三份 js(commonJs
、es module
、umd
),以及一份壓縮後的 css(dist/tua-ui.css
)。
{
"main": "dist/TuaUI.cjs.js",
"module": "dist/TuaUI.es.js",
"browser": "dist/TuaUI.umd.js",
}
複製程式碼
完整載入
大部分 ui 庫都支援完整載入,和把大象裝冰箱一樣簡單(但 vux 只支援按需載入):
- 引入 js
- 引入 css
- 安裝外掛
import TuaUI from '@tencent/tua-ui'
import '@tencent/tua-ui/dist/tua-ui.css'
Vue.use(TuaUI)
複製程式碼
因缺思廳的是 cube-ui 把基礎樣式也寫成 Vue 外掛,導致按需引入的時候還要單獨引入
Style
,emmmmmmmmm...
import {
/* eslint-disable no-unused-vars */
Style, // <-- 不寫這行按需引入時就沒基礎樣式
Button
} from 'cube-ui'
複製程式碼
按需載入
ui 庫若是隻能完整載入,顯然會打包多餘程式碼。
所以各種庫一般都支援按需載入元件,大概分以下幾種。
- muse-ui、iview、ant-design-vue、vant 通過 babel-plugin-import 外掛實現。
- element 通過 babel-plugin-component(fork 自 babel-plugin-import)外掛實現。
- vux 通過自己的 vux-loader 實現。
- cube-ui 通過配置 webpack 實現。
tree-shaking
webpack 其實在打包的時候是支援 tree-shaking 的,那麼我們能不能直接引用原始碼實現按需載入呢?
注意原始碼必須滿足 es 模組規範(import、export)。
import { TuaToast } from '@tencent/tua-ui/src/'
Vue.use(TuaToast)
複製程式碼
嘗試打包,發現 tree-shaking
並沒有起作用,還是打包了所有程式碼。
sideEffects
其實問題出在沒有在 ui 庫的 package.json
中宣告 sideEffects
屬性。
在一個純粹的 ESM 模組世界中,識別出哪些檔案有副作用很簡單。然而,我們的專案無法達到這種純度,所以,此時有必要向 webpack 的 compiler 提供提示哪些程式碼是“純粹部分”。 —— 《webpack 文件》
注意:樣式部分是有副作用的!即不應該被 tree-shaking
!
若是直接宣告 sideEffects
為 false
,那麼打包時將不包括樣式!所以應該向下面這樣配置:
{
"sideEffects": [ "*.sass", "*.scss", "*.css" ],
}
複製程式碼
vuepress 元件樣式
用 vuepress 寫文件的時候,一般會在 docs/.vuepress/components/
下寫一些全域性元件。
開發時沒啥問題,但是發現一個坑:打包文件時發現元件裡的樣式 <style>
全丟了。
猜一猜原因是什麼?
這口鍋就出在上一節的 sideEffects
,詳情看這個 issue。解決方案就是在 sideEffects
里加一條 "*.vue"
即可。
測試資料
下面咱們打包一下安裝了 ui 庫的專案,看看按需載入的效果怎麼樣。
- Origin
- dist/js/chunk-vendors.71ea9e72.js ----- 114.04 kb
- TuaToast
- dist/js/chunk-vendors.beb8cff5.js ----- 115.03 kb
- dist/css/chunk-vendors.97c93b2d.css ----- 0.79 kb
- TuaIcon
- dist/js/chunk-vendors.25d8bdbd.js ----- 115.00 kb
- dist/css/chunk-vendors.eab6517c.css ----- 6.46 kb
- TuaUI
- dist/js/chunk-vendors.6e0e6390.js ----- 117.39 kb
- dist/css/chunk-vendors.7388ba27.css ----- 8.04 kb
總結一下就是:
- 原始專案的 js 打包出來為
114.o4kb
- 只新增
TuaToast
後 js 增加了0.99kb
,css 增加了0.79kb
- 只新增
TuaIcon
後 js 增加了0.96kb
,css 增加了6.46kb
- 新增完整
TuaUI
後 js 增加了3.35kb
,css 增加了8.04kb
可以看出按需載入還是有效果的~
以上 to be continued...