從0到1教你搭建前端團(tuán)隊(duì)的組件系統(tǒng)(高級(jí)進(jìn)階必備)

前言
隨著vue/react這類以數(shù)據(jù)驅(qū)動(dòng)為主的web框架的不斷完善和壯大,越來(lái)越多的前端團(tuán)隊(duì)開始著手搭建內(nèi)部的組件庫(kù)。雖然目前市面上已經(jīng)有很多功能強(qiáng)大且完善的組件庫(kù)供我們使用,比如基于react的開源組件庫(kù)ant-design,material,又比如基于vue的開源組件庫(kù)elementUI,iView等。
我們?cè)陂_發(fā)管理系統(tǒng)或者中臺(tái)產(chǎn)品時(shí),完全可以使用這種第三方庫(kù)來(lái)開發(fā),因?yàn)槭紫绕浞?wù)的用戶群體比較小眾,一般是企業(yè)或者運(yùn)營(yíng)人員來(lái)使用,重點(diǎn)在于功能和業(yè)務(wù),所以在B端產(chǎn)品比較適合;另一點(diǎn)就是設(shè)計(jì)要求相對(duì)于C端產(chǎn)品會(huì)低一些,因?yàn)锽端產(chǎn)品或者管理系統(tǒng)風(fēng)格統(tǒng)一簡(jiǎn)單反而會(huì)降低使用者的學(xué)習(xí)成本。所以對(duì)于上述情況,我們完全可以使用ant-design-pro或者element-admin-vue這類集成管理框架開開發(fā)。我們使用第三方組件庫(kù)搭建一個(gè)企業(yè)級(jí)應(yīng)用是完全沒有問題的,但是另一方面,隨著我們對(duì)用戶體驗(yàn)以及網(wǎng)站性能的要求越來(lái)越高,流量及金錢,速度即王道,對(duì)于專注于做C端的企業(yè)來(lái)說,盡可能的減少用戶等待才能留住更多的用戶,比如我們?cè)谀硨?,某東上買一個(gè)商品,結(jié)果我們花了一分鐘商品列表還沒有出來(lái)(形容的有點(diǎn)夸張),這種情況下客戶可有可能直接選擇某拼了。很明顯像ant-design和elementUI這樣的組件不適合做C端產(chǎn)品,因?yàn)轶w積太大了,除非用高性能服務(wù)器或者其他方式彌補(bǔ)。所以說采用輕量級(jí)組件庫(kù)對(duì)于C端項(xiàng)目來(lái)說有以下幾點(diǎn)好處:- 打包體積小,高度可控
- 采用內(nèi)部組件庫(kù)安全性更高,防止嵌入攻擊
- 構(gòu)建和開發(fā)更靈活,且組合型更高
你將收獲
- 如何從0搭建一個(gè)組件庫(kù)
- 前端組件系統(tǒng)設(shè)計(jì)思路和模式
- 組件庫(kù)的劃分及設(shè)計(jì)思路
- 組件庫(kù)的package.json文件配置說明
- 將組件庫(kù)部署到github并發(fā)布到npm上
正文
1. 開發(fā)組件庫(kù)的幾種方式
目前我們開發(fā)組件庫(kù)的方式有很多,只要根據(jù)npm發(fā)包原則去配置就好了,我們可以用webpack自己大家一個(gè)library,也可以直接使用create-react-app/vue-cli3來(lái)快速改造一個(gè)組件庫(kù)的腳手架,或者采用之前比較火且自動(dòng)集成tree-shaking的rollup,這些方式都可以搭建我們組件庫(kù)的腳手架。關(guān)于如何使用webpack4.0和rollup,可以參考筆者的以下幾篇文章:- 前端組件/庫(kù)打包利器rollup使用與配置實(shí)戰(zhàn)
- 基于create-react-app打包編譯自己的第三方UI組件庫(kù)并發(fā)布到npm
- 用 webpack 4.0 擼單頁(yè)/多頁(yè)腳手架 (jquery, react, vue, typescript)
其實(shí)還有一種最快的方式就是直接去ant-design或者elementUI的github倉(cāng)庫(kù),把代碼copy下來(lái)改成自己的組件庫(kù)腳手架,當(dāng)然,這也不是隨便就能改好的,如果想嘗試這種方案,建議大家先學(xué)好typescript和webpack。
筆者這里采用的是目前比較流行的工具鏈umi,umi的father專門是提供組件庫(kù)或者工具庫(kù)打包的集成工具,我們只需要更改配置文件就能輕松搭建一款自帶說明文檔的組件庫(kù)。筆者接下來(lái)會(huì)具來(lái)教大家如何使用它。
2. 前端組件系統(tǒng)設(shè)計(jì)思路和模式

以上是筆者畫的一個(gè)簡(jiǎn)陋的分層圖,我們可以看到最底層的是我們的基礎(chǔ)視圖組件,它是上層建筑的基石。對(duì)于一個(gè)包含很多子系統(tǒng)的復(fù)雜的項(xiàng)目系統(tǒng)來(lái)說,要想設(shè)計(jì)一個(gè)好的架構(gòu),第一步就是合理劃分組件,組件的粒度拆成的足夠細(xì),這樣才能最大限度的復(fù)用組件。
對(duì)于任何一個(gè)復(fù)雜系統(tǒng)來(lái)說,最重要的就是實(shí)現(xiàn)錯(cuò)綜復(fù)雜的業(yè)務(wù)功能,但是不同模塊或者子系統(tǒng)之間很多業(yè)務(wù)往往是相通的或者相似的,如果這個(gè)時(shí)候我們每個(gè)頁(yè)面對(duì)于實(shí)現(xiàn)類似的業(yè)務(wù)場(chǎng)景都去重復(fù)去寫一遍業(yè)務(wù)代碼,那完全是沒必要的,對(duì)于可維護(hù)性來(lái)說也是一種打擊,所以基于這種場(chǎng)景我們的?業(yè)務(wù)組件?就很有必要出場(chǎng)了。我們可以把功能或者需求類似的有機(jī)體封裝成一個(gè)業(yè)務(wù)組件,并對(duì)外暴露接口來(lái)實(shí)現(xiàn)靈活的可定制性,這樣的話我們就可以再不同頁(yè)面不同子系統(tǒng)中復(fù)用同樣的邏輯和功能了。同理,不同頁(yè)面中往往有可能出現(xiàn)視覺或者交互完全相同或者類似的區(qū)塊,為了提高可復(fù)用性和提高開發(fā)效率,我們往往會(huì)基于基礎(chǔ)組件和業(yè)務(wù)組件再進(jìn)行一次封裝,讓其成為一個(gè)獨(dú)立的區(qū)塊以便直接復(fù)用。通過這樣一層層封裝,我們就逐漸搭建了一套完整的組件化系統(tǒng),基于這種模式的開發(fā)往往也是一個(gè)好的前端架構(gòu)的開始。但要注意一點(diǎn)就是高層次的組件一定會(huì)依賴低層次的組件,但是低層次的組件不可以包含高層次的組件。(聽起來(lái)有點(diǎn)像rudex的單向數(shù)據(jù)流法則),他們的關(guān)系就好像下圖:
3. 組件庫(kù)的劃分及設(shè)計(jì)思路
組件庫(kù)的劃分其實(shí)可以參考成熟組件庫(kù)劃分。由于業(yè)務(wù)組件和區(qū)塊劃分完全取決于不同公司的實(shí)際項(xiàng)目情況,這里不能形成一套統(tǒng)一的思維框架,所以我這里說的組件庫(kù)劃分主要指基礎(chǔ)組件庫(kù)的劃分。我們先來(lái)看看antd的劃分,它劃分為:通用組件,布局組件,導(dǎo)航組件,數(shù)據(jù)錄入和數(shù)據(jù)展示組件,反饋型組件和其他。elementUI的組件劃分為:基礎(chǔ)組件,表單組件,數(shù)據(jù)呈現(xiàn)組件,通知類組件,導(dǎo)航類組件和其他,這些分類都是非常合理的劃分,所以我們?cè)谠O(shè)計(jì)組件庫(kù)時(shí)也可以參考或者直接使用,具體總結(jié)如下:
- 通用型組件: 比如Button, Icon等.
- 布局型組件: 比如Grid, Layout布局等.
- 導(dǎo)航型組件: 比如面包屑Breadcrumb, 下拉菜單Dropdown, 菜單Menu等.
- 數(shù)據(jù)錄入型組件: 比如form表單, Switch開關(guān), Upload文件上傳等.
- 數(shù)據(jù)展示型組件: 比如Avator頭像, Table表格, List列表等.
- 反饋型組件: 比如Progress進(jìn)度條, Drawer抽屜, Modal對(duì)話框等.
- 其他業(yè)務(wù)類型
需求分析

- 功能設(shè)計(jì)及實(shí)現(xiàn)思路

- 健壯性與組件測(cè)試

因?yàn)镸odal設(shè)計(jì)是組件設(shè)計(jì)里一個(gè)很典型的案例,如果想學(xué)習(xí)具體實(shí)現(xiàn)細(xì)節(jié),可以在讀完本文之后移步手摸手實(shí)現(xiàn)一個(gè)輕量級(jí)可擴(kuò)展的模態(tài)框(Modal)組件
4. 從0搭建一個(gè)組件庫(kù)
這一步是文章的重點(diǎn),我們將會(huì)了解到如何使用umi/father來(lái)搭建團(tuán)隊(duì)的組件庫(kù)。至于umi這個(gè)前端集成解決方案,筆者從它的架構(gòu)中受到了很多啟發(fā),并且基于umi+dva+react的前端開發(fā)流程應(yīng)用非常廣泛,感興趣的朋友可以研究學(xué)習(xí)一下。
4.1?father介紹
官方介紹就一句話:基于rollup和docz的庫(kù)打包工具。它的特點(diǎn)主要有:
- 基于 docz 的文檔功能
- 基于 rollup 和 babel 的組件打包功能
- 支持 TypeScript
- 支持 cjs、esm 和 umd 三種格式的打包
- esm 支持生成 mjs,直接為瀏覽器使用
- 支持用 babel 或 rollup 打包 cjs 和 esm
- 支持多 entry
- 支持 lerna
- 支持 css 和 less,支持開啟 css modules
- 支持 test
- 支持用 prettier 和 eslint 做 pre-commit 檢查
4.2 使用father搭建組件庫(kù)
其實(shí)father的使用非常簡(jiǎn)單,首先我們先安裝一下father:
npm?install?father?-D
或者使用yarn安裝:
yarn?add?father
接下來(lái)我們可以在package.json里配置如下腳本來(lái)使用:
# 打包庫(kù)$ father build# 開發(fā)環(huán)境下啟動(dòng)文檔服務(wù)$ father doc dev# 打包編譯文檔$ father doc build# 將文檔部署到github$ father doc deploy# 組件庫(kù)測(cè)試及測(cè)試覆蓋率$ father test$?father?test?--coverage
這里拿筆者之前已經(jīng)發(fā)布到npm的組件庫(kù)xui——基于react的輕量級(jí)UI組件庫(kù)來(lái)舉例。首先我們看看xui的package.json中的script腳本如何配置的:
"scripts": { "dev": "npx --max_old_space_size=8096 father doc dev --host 0.0.0.0", "build": "father build", "build:doc": "father doc build", "deploy": "father doc deploy"??}


這些內(nèi)容都是我們提前寫好的mdx文檔,語(yǔ)法類似于markdown,只不過增加了對(duì)react組件的編譯支持,其實(shí)使用起來(lái)很簡(jiǎn)單,我們只需要把react組件包裹在Playground容器里就好了,具體使用可以參考以下方式:
---name: Buttonroute: /Buttonorder: 3sidebar: true---import { Playground } from 'docz'import Button from './index'# Button#### 基本用法
頭部的信息我需要介紹一下:
- name 組件的名稱,也就是顯示在左部導(dǎo)航欄里的導(dǎo)航文本
- route 組件頁(yè)面的路由
- order 組件在導(dǎo)航條中顯示的順序
- siderbar 當(dāng)前頁(yè)面是否顯示導(dǎo)航條

接下來(lái)介紹我們最重要的部分,.fatherrc.js文件的配置。初始化father項(xiàng)目時(shí)會(huì)自動(dòng)生成一個(gè)默認(rèn)配置,但是大多數(shù)情況下我們都需要自定義配置,官網(wǎng)的配置參數(shù)也很多,可以找到適合自己團(tuán)隊(duì)的配置,這里我主要介紹xui組件庫(kù)的配置細(xì)節(jié)。先看看配置代碼:
//?.fatherrc.jsconst options = {entry: 'src/index.js',doc: {title: 'xu_ui',themeConfig: { mode: 'light' },base: '/xu_ui'},extraBabelPlugins: [['babel-plugin-import', {libraryName: 'antd',libraryDirectory: 'es',style: true,}]],// cssModules: true,extractCSS: true,lessInBabelMode: true,runtimeHelpers: true,esm: 'babel',cjs: 'babel',autoprefixer: {browsers: ['ie>9', 'Safari >= 6'],}};export?default?options;
entry?主要用來(lái)定義組件庫(kù)的入口位置,通常我們會(huì)放在src目錄下,以下是xui項(xiàng)目中src的目錄結(jié)構(gòu):


大家可以參考一下,我們還可以在組件目錄下加測(cè)試代碼,這里就不舉例了。
- doc?主要用來(lái)配置文檔的標(biāo)題,主題色以及根路由
- extraBabelPlugins?主要用來(lái)配置額外的babel插件,比如組件庫(kù)的按需導(dǎo)入。xui雖然沒用用到antd,但是大家如果有基于antd二次開發(fā)業(yè)務(wù)組件或者區(qū)塊時(shí),可以按照如上配置去按需導(dǎo)入第三方組件庫(kù),這樣可以極大的降低代碼體積
- cssModules?是否開啟css Module,這個(gè)按團(tuán)隊(duì)需求來(lái)定制,可以不用配置
- extractCSS?是否將css抽離成單獨(dú)的css文件,這個(gè)也是看組件庫(kù)的體量,不過建議最好配置上
- lessInBabelMode?在 babel 模式下做 less 編譯,基于 gulp-less,默認(rèn)不開啟
- runtimeHelpers?是否把 helper 方法提取到 @babel/runtime 里,推薦開啟,能節(jié)約不少代碼體積
- esm?是否輸出 esm 格式,以及指定 esm 格式的打包方式等,筆者這里使用babel的打包方式
- cjs?是否輸出 cjs 格式,以及指定 cjs 格式的打包方式等,筆者這里使用babel的打包方式
- autoprefixer?主要用來(lái)配置打包后的組件對(duì)瀏覽器的兼容版本
4.3 編寫組件說明文檔
組件說明文檔是讓其他人了解組件庫(kù)的關(guān)鍵環(huán)節(jié),包括組件庫(kù)的適用范圍(pc端,移動(dòng)端,輕量級(jí)還是重量級(jí)),兼容瀏覽器的版本,設(shè)計(jì)原則和背景,以及社區(qū)生態(tài),使用方法等。所以組件庫(kù)說明文檔的編寫也是非常重要的,大家具體可以參考antd或者element的說明文檔,可以說是寫的非常專業(yè)了。如下是antd的例子:

大家可以參考以上筆者整理的核心部分去寫組件說明文檔。
5. 組件庫(kù)的package.json文件配置說明
如果你在為團(tuán)隊(duì)開發(fā)私有組件庫(kù),那么完全不需要在意接下來(lái)筆者寫的內(nèi)容,但是如果你要開發(fā)一個(gè)開源的,大家都能使用的組件庫(kù),一定要注意以下幾點(diǎn)的編寫:
- name?package的包名,讓人一眼就能知道這個(gè)庫(kù)是用來(lái)做什么的
- description?庫(kù)的描述,一個(gè)精準(zhǔn)而具體的組件庫(kù)的描述有利于人們?cè)趎pm或者github上的搜索,有點(diǎn)類似于SEO的感覺,沒錯(cuò),這就是組件庫(kù)的SEO
- keywords?組件庫(kù)的關(guān)鍵字說明,這一點(diǎn)也很重要,直接影響者用戶對(duì)我們組件庫(kù)的搜索排名
- homepage?組件庫(kù)的主頁(yè)地址,更有利于用戶了解組件庫(kù)的用法,功能等 完整的在線例子可以參考:xui——基于react的輕量級(jí)UI組件庫(kù)
6. 將組件庫(kù)部署到github并發(fā)布到npm上
首先我們需要在package.json中配置github的地址:
"repository": { "type": "git", "url": "git+https://github.com:MrXujiang/xu_ui.git"??}
大家可以復(fù)制以上代碼改成自己的倉(cāng)庫(kù)地址。
其次我們需要登錄進(jìn)npm官網(wǎng),如果你還沒有賬號(hào)可以直接申請(qǐng)一個(gè),也很簡(jiǎn)單,然后在終端通過命令行登錄。
接下來(lái)我們執(zhí)行以下幾個(gè)命令就能將自己的組件庫(kù)打包發(fā)布到npm上了:
// 打包編譯組件庫(kù)yarn build// 編譯組件庫(kù)文檔,該步驟可省略yarn build:doc// 部署組件庫(kù)文檔到github, 該步驟可省略yarn deploy// 發(fā)布到npm上npm?publish?--access?public
以上的yarn命令具體實(shí)現(xiàn)可參考xui的package.json,也可以自己配置。經(jīng)過上述步驟我們就成功將自己的組件庫(kù)發(fā)布到npm上了,是不是很簡(jiǎn)單呢?
npm i @alex_xu/xui// 導(dǎo)入xuiimport { Button, Skeleton, Empty, Progress, Message, Tag, Switch, Drawer, Badge, Alert}?from?'@alex_xu/xui'
該組件庫(kù)支持按需導(dǎo)入,我們只需要在項(xiàng)目里配置babel-plugin-import即可,具體配置如下:
// .babelrc"plugins": [ ["import", { "libraryName": "@alex_xu/xui", "style": true }]]

組件系統(tǒng)與微前端架構(gòu)初探
筆者本篇文章并不會(huì)將微前端架構(gòu)的知識(shí),但是既然涉及到組件庫(kù),就一定要形成一個(gè)知識(shí)閉環(huán),筆者特意畫了如下腦圖,供前端朋友或者正準(zhǔn)備走向微前端架構(gòu)的團(tuán)隊(duì)一些參考:
推薦閱讀
我的公眾號(hào)能帶來(lái)什么價(jià)值?(文末有送書規(guī)則,一定要看)
每個(gè)前端工程師都應(yīng)該了解的圖片知識(shí)(長(zhǎng)文建議收藏)
為什么現(xiàn)在面試總是面試造火箭?
