前端 UI 組件庫搭建指南

近期負責公司內部 React UI 組件庫的搭建,過程當中踩了很多坑,主要仍是對整個搭建過程沒有一個比較清晰的概念,本文章就做爲整個搭建過程的一個總結吧。前端

本文將詳細講述搭建一個前端 UI 組件庫須要涉及的流程和相關知識、工具,其中參考了一些主流開源組件庫的作法,閱讀大約須要15分鐘。vue

Monorepo?

Monorepo(Monolithic Repositories)是目前比較流行的一種將多個項目的代碼放在同一個庫統一管理的代碼管理組織方式,這種方式可以比較方便地進行版本管理和依賴管理,一般配合 lerna 管理多個 package。node

那麼,UI 組件庫需不須要使用 Monorepo 這種模式呢?webpack

縱觀目前各大開源項目,像 Reactbabel 等生態較爲豐富的項目,都是以」一個主包,多個從包「構建的生態系統,比較適合採用 Monorepo 的方式管理複雜的依賴關係,例如 React 的 packagesgit

但對於 UI 組件庫來講,每一個組件做爲一個個獨立的單元存在,相互之間的依賴通常比較少,因此對於組件庫自身沒有必要採用 Monorepo 的方式拆分多個 package。那以組件庫爲主包、各類自研的工具庫做爲從包的方式可使用 Monorepo 進行管理嗎?答案是能夠的,目前有讚的 zent 就是採用這種方式:github

因此到底應不該該採用 Monorepo 的模式呢?個人建議是,若是僅僅是想造好組件庫這一個輪子,而且沒有依賴關係比較複雜的其餘庫須要統一管理,那就直接以一個 package 來管理便可,怎麼簡單怎麼來!web

目錄結構

在肯定組件庫的目錄結構以前,先大體捋一下組件庫須要有哪些組成部分:shell

  • 源代碼
  • 示例代碼
  • 文檔
  • 打包結果
  • 測試代碼
  • 打包構建配置、腳本
  • 配置文件(babel、eslint、jest 等)
  • 自動化腳本
  • ...

組成的目錄結構大體以下:npm

├── build                           // 打包腳本
├── docs                            // 文檔部署目錄(Github Pages)
├── examples                        // 示例代碼(本地開發環境)
├── lib                             // 打包結果
├── scripts                         // 自動化腳本
├── site                            // 文檔靜態站點
├── src                             // 組件庫源碼
    ├── components                  // 全部組件
        ├── [componentName]         // 單個組件
            ├── __tests__           // 組件測試文件
    ├── styles                      // 樣式
    ├── types                       // 類型聲明文件
├── tests                           // 測試
├── .babelrc                        // 插件
├── .eslintrc                       // eslint 配置
├── .publish-ci.yml                 // npm 包發佈、站點部署 CI 腳本
├── jest.config.js                  // Jest 配置文件
└── package.json                    // package.json
複製代碼

組件開發

本地開發環境

首先須要一套可運行的開發環境,以供咱們在本地調試、運行組件代碼。json

參考大多數 UI 組件庫的作法,能夠examples 下的示例代碼組織起來並暴露一個入口,使用 webpack 配置一個 dev-server,後續對組件的調試、運行都在此 dev-server 下進行。

或者也可使用腳手架工具搭一個單頁應用做爲本地開發環境,可省去配置 webpack 的麻煩。方法其實有不少種,目的是爲了讓組件在本地 Run 起來,以方便項目的 developers 和 contributors。

最後,記得將 dev-server 的啓動腳本加入 npm scripts 中,例如:

package.json

組件初始化

在開發一個組件以前,須要建立組件目錄、建立組件文件、初始化組件模板、建立測試目錄/文件等一系列繁瑣又重複的工做,這些其實均可以使用腳本自動化實現。

能夠在 scripts 目錄下寫一個 node 腳本實現以上過程,並在 package.json 中添加 scripts 腳本加入到 npm 工做流中:

package.json

scripts/new_component.js:

單元測試

UI 組件做爲高度抽象的基礎公共組件,編寫單元測試是頗有必要的。一方面,單元測試可以覆蓋到一些端到端測試覆蓋不到的點,另外一方面,也能提升組件代碼的可維護性,保證代碼質量。

框架選型

關於單元測試框架的選型,以 React UI 組件庫爲例,目前比較流行的組合是 Jest + Enzyme

Jest

Jest 是 Facebook 開源的一個前端測試框架,自帶斷言庫,配置簡易,提供了 JSDOM、Mock 系統、快照測試、異步代碼測試、靜態分析結果等測試功能。

Jest 會在如下幾個地方尋找測試文件:

  • __tests__ 目錄下後綴爲 .js 的文件
  • 後綴爲 .test.js 的文件
  • 後綴爲 .spec.js 的文件

通常會把測試文件放在對應的組件同級目錄下,這樣在語義上是有意義的,而且引入路徑也更短些。

Enzyme

Enzyme 是 Airbnb 開源的一個 React 測試類庫,提供了一套簡潔又強大的 DOM 處理 API。Enzyme 是對官方測試工具庫 ReactTestUtils 的二次封裝,並內置了 Cheerio(一個號稱 「服務端 JQuery」 的爬蟲庫)。

Jest + Enzyme

如下是一個使用 Jest + Enzyme 編寫的單元測試示例:

將測試流程加入工做流

package.json

打包

對於打包後的文件,統一放在 lib 目錄下,同時記得要在 .gitignore 中加上 lib 目錄,避免將打包結果提交到代碼庫中。

提供 umd 規範的包

umd 兼容了 AMDCommonJS 兩種模塊化規範,可同時支持瀏覽器、Node 兩種宿主環境,經過指定 Webpack 配置中的 output.libraryTarget 字段爲 umd 便可:

libraryTarget 還可根據須要設置爲 windowglobalcommonjs 等值應對不一樣的打包場景,具體參考 Webpack 配置文檔

按需加載

在 UI 組件庫的使用場景中,每每有時候只需引入個別組件而非全量組件,那麼就要求組件庫須要有可以按需加載的能力,支持相似以下方式的引入語法:

實現按需加載,通常有兩種方式: Tree Shaking 和單獨打包組件。

Tree Shaking

若是經過 Tree Shaking 實現按需加載,那麼通過 babel 編譯的 umd 包確定是沒法知足的,須要另外提供一份 ES6 Module 規範的包。

可是!Webpack 的 libraryTarget 不支持 ES6 Module 規範的包的導出。只能藉助於其餘打包工具例如 rollup 來導出 ES6 Module 規範的包,並配合 package.json 指定 module 字段爲 ES6 Module 規範的包的路徑,以實現使用 ES6 的方式引入包時讀取到的是提供的 ES6 Module 規範的包。

單獨打包組件

若沒法導出 ES6 Module 規範的包,也能夠採用目前大多數組件庫的作法:將組件單獨打包

將組件單獨打包須要在 Webpack 中配置多個entry,大體配置以下:

使用以上配置最終會將組件打包到不一樣的目錄下,這樣就能夠支持按需引入的方式加載組件了:

可是,要實現上文提到的 import { Alert, Button } from 'ui-library' 這種引入方式,還須要藉助第三方的 babel 插件來實現引入語句的轉換,目前使用比較多的有:


對於 umd 和按需加載的兩種打包方式,須要分別提供對應的 npm script,例如:

文檔

組件庫的文檔通常都是對外可訪問的,所以須要部署到服務器上,同時也需具有本地預覽的功能。

文檔生成器

你能夠選擇本身搭一個用於展現文檔的站點,也可使用文檔生成器來生成文檔站點,比較推崇使用文檔生成器。能夠根據本身比較擅長的技術棧選擇特定的文檔生成器,目前主流的文檔生成器有:

  • Docz:React 技術棧,MDX(Markdown + jsx)語法,基於 Gatsby.js
  • Storybook:支持 Vue/React/Angular 等,提供功能豐富的 addons 插件加強文檔交互體驗。
  • React Styleguidist:React 技術棧,支持在 md 文件中解析 js/jsx 代碼塊。
  • VuePress:Vue 技術棧,支持在 md 文件中插入 Vue 組件。

以上幾種文檔生成器均支持在 markdown 文件中插入 js/jsx 或特定的組件標籤,區別在於語法風格不一樣。若是使用的是 React 技術棧,只能說,Docz 所引入的 MDX 語法,真香!甩個圖隨意感覺下:

文檔部署

Github Pages

若是項目代碼託管在 Github 上的話,能夠將文檔站點部署在 Github Pages 上。

Github Pages 提供了三種模式,你能夠將靜態站點放在如下三個位置:

  • master 分支
  • master 分支的 docs 目錄
  • gh-pages 分支

例如在 Github 項目的 Settings 下,將 Github Pages 的 Source 改成 master branch/docs folder

經過訪問 <username>.github.io/<repository-name> 便可訪問到 docs 目錄下的資源。

CI/CD

關於文檔站點的持續集成和自動化部署,能夠藉助官方或第三方提供的 CI/CD 工具,例如:

  • Github + Travis CI
  • Github + Github Actions
  • Gitlab + Gitlab CI/CD

發佈

組件庫的某個版本完成開發工做後,須要將包發佈到 npm 上。

關於 package.json 你須要知道的

package.json 中有幾個字段值得關注:

name

  • 發佈到 npm 上的包名
  • 安裝時的包名

格式:英文小寫,中劃線或下劃線分隔。

version

版本號,符合語義化版本規則,即 major.minor.patch

  • major:主版本號,不兼容的修改
  • minor:次版本號,向下兼容的新功能
  • patch:修訂號,向下兼容的問題修復

main、unpkg、module/jsnext:main

main包的入口。例如,Node 環境下使用 import pkg from 'package-name' 導入的就是 main 定義的入口文件,能夠是 CommonJS 格式或者 umd 格式。

unpkg 定義瀏覽器環境使用的入口,通常格式爲 name.min.js

module 定義用 ES6 模塊打包的入口,通常格式爲 name.esm.js

示例:

description、keywords

descriptionkeywords 分別定義包的描述和關鍵字,有助於包的檢索,並在檢索結果中顯示。

author

做者,通常格式是 ${name} ${email}


除了以上字段以外,根目錄下的 README.md 內容會在 npm 包的詳情頁展現。

發佈前

執行測試

在發佈以前,須要確保 UI 組件庫的代碼可以經過全部編寫的測試用例,運行先前加入工做流的 npm run test 命令便可。

打包構建

在發佈 npm 包以前,須要執行打包構建,確保 lib 目錄下的內容包含當前須要發佈的內容。

更新版本號

使用 npm version 命令自動計算下一個版本號,並將該版本號更新到 package.json 文件中。能夠手動指定符合語義化版本規則版本號,也可使用 npm version 提供的一些快捷命令自動更新版本號。npm version 的語法以下:

npm version [<newversion> | major | minor | patch | premajor | preminor | prepatch | prerelease [--preid=<prerelease-id>] | from-git]
複製代碼

若是在一個 Git 倉庫下執行 npm version,會默認新增一個同名 Tag,這一默認行爲能夠用 npm --no-git-tag-version version 禁止。

更多關於 npm version 的用法能夠參考官方文檔

npm 包發佈

  • npm 官網 註冊一個帳號(或者使用 npm adduser 註冊)
  • 查看本地 .npmrc 配置,確保 registry=https://registry.npmjs.org/(可經過 npm config edit 查看或編輯)
  • 運行 npm login 在本地登陸 npm 帳號
  • 執行 npm publish

以上過程發佈的是一個 unscoped 包,當發佈的包跟 npm 上現有的包存在命名衝突時,就須要發佈一個特定做用域下的 scoped 包:

  • 包名格式須要加上做用域:@scope/package-name
  • 在發佈時指定爲公共發佈:npm publish --access public,以免被默認識別爲私包(私包是收費的T T)

打標籤

咱們須要爲特定的版本運行 git tag -a [tagname] 打上 Tag,並運行 git push origin [tagname] 顯式將該 Tag 推送至遠程。

這一過程能夠在 npm 包發佈以前,也能夠在 npm 發佈以後,在後面的自動化發佈中會涉及到這一點。

自動化發佈

若是每次發佈都須要運行測試、打包構建、更新版本號、npm 包發佈等流程,未免有些繁瑣,須要藉助一些自動化的手段將上述流程串連起來,實現自動化發佈。

自動化發佈 npm 須要有一個觸發點,能夠是運行 npm script 時觸發 prepost 鉤子實現全流程的自動化,例如:定義 preversionpostversion 鉤子腳本,在執行 npm version 先後會分別執行這兩個腳本。也能夠配合 CI/CD 工具,例如在 CI/CD 腳本中監聽 master 分支的 tags 推送,一旦有新 tag 推送至遠程,就執行上述的發佈流程,包括文檔站點的部署也能夠在這裏實現。

維護

CHANGELOG.md

通常開源項目會將 Changelog 維護到一個 Markdown 文件裏,例如 CHANGELOG.md

若是你的 commit message 使用的是 Angular 規範 ,那麼可使用 conventional-changelog 工具自動根據 commit message 生成 CHANGELOG.md,如下三種 type 的 commit 會被寫入 CHANGELOG.md:

  • feat
  • fix
  • Breaking change

一樣,須要將該流程加入到 npm script 工做流中:

以上命令會在 CHANGELOG.md 頭部追加上次發佈以來符合規範的 commit message。

CONTRIBUTING.md

若是你想讓更多的開發者參與到組件庫的共建,能夠以 Markdown 文檔的形式提供一份簡單的 Contributing Guide,命名爲 CONTRIBUTING.md,大體包含如下內容:

  • Issue/Pull Request 規範
  • 開發環境搭建
  • 開發規範

總結

本文主要總結了搭建一個前端 UI 組件庫所涉及到各個步驟:

  • 代碼組織方式:是否應該採用 MonoRepo?
  • 目錄結構
  • 組件開發:本地開發環境、組件初始化
  • 單元測試:框架選型(Jest + Enzyme,以 React UI 組件庫爲例)
  • 打包:提供 umd 規範的包、按需加載的兩種打包方式
  • 文檔:文檔生成器對比、文檔部署
  • 發佈:package.json 相關字段說明、發佈前的準備工做、npm 發佈流程、自動化發佈
  • 維護:CHANGELOG.md 自動生成、CONTRIBUTING.md 主要內容

參考:

相關文章
相關標籤/搜索