開發模式

預覽 demo

在開發一個 ui 組件庫時,肯定需要一邊預覽 demo,一邊修改代碼。

常見的解決方案是像開發一般項目一樣使用 webpack-dev-server 預覽組件,比如通過 vue-cli 初始化項目,或者自己配置腳本。

文藝一點兒地可能會用到 parcel 來簡化 demo 的開發配置(比如 muse-ui)。

展示文檔

作為一個 ui 組件庫,也肯定要有自己的組件展示文檔。

一般業界常見方案是自己開發展示文檔...

但這樣會帶來一個組件庫和文檔如何同步的問題。

為何不用 vuepress?

由於 vuepress 支持在 markdown 中插入組件,所以我們其實可以很自然地邊寫文檔邊開發組件。

從開發步驟上來說,甚至可以先寫文檔說明,再具體地編寫代碼實現組件功能。這樣一來文檔即是預覽 demo,與組件開發可以同步更新。

p.s. React 的組件文檔可以試試這倆庫:

  • docz
  • doc-scripts

類型聲明

在開發和使用過程中如果對於一些對象、方法的參數能夠智能提示,豈不美哉?

如何實現呢?

其實就是在相應文件夾中添加組件相關的類型聲明(*.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(commonJses moduleumd),以及一份壓縮後的 css(dist/tua-ui.css)。

{
"main": "dist/TuaUI.cjs.js",
"module": "dist/TuaUI.es.js",
"browser": "dist/TuaUI.umd.js",
}

完整載入

大部分 ui 庫都支持完整載入,和把大象裝冰箱一樣簡單(但 vux 只支持按需載入):

  1. 引入 js
  2. 引入 css
  3. 安裝插件

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

若是直接聲明 sideEffectsfalse,那麼打包時將不包括樣式!所以應該像下面這樣配置:

{
"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...

參考資料

  • vuepress 在 Markdown 中 使用 Vue
  • muse-ui
  • iview
  • element
  • ant-design-vue
  • babel-plugin-import
  • babel-plugin-component
  • vux
  • vant
  • vux-loader
  • cube-ui
  • cube-ui 的後編譯和普通編譯
  • tree-shaking
  • rollup-plugin-vue
  • source-map 報錯的問題
  • vuepress sideEffects issue

推薦閱讀:

相关文章