在 React 大生態下,一個比較成熟的前端團隊,都會面對一個問題:如何提高團隊的開發效率?
一個系統擁有大量的業務場景和業務程式碼,相似的頁面和程式碼層出不窮,如何管理和抽象這些相似的程式碼和模組,這肯定是諸多團隊都會遇到的問題。 不斷的拷程式碼?還是抽象成 UI 元件或業務元件?顯然後者更高效。
那麼現在就面臨一個選擇:一是選擇 React 生態中已有的元件庫,例如 antDesign、Material-UI 等比較成熟的元件庫;二是團隊再開發一套屬於自己的元件庫。有贊前端團隊選擇了後者,產出並開源了 Zent ,Zent 提供了一整套基礎的 UI 元件以及常用的業務元件,目前我們有 45+ 元件,這些元件都已經在有讚的各類 PC 業務中廣泛使用。本文我們就來聊一聊如何開發一套優秀的 React 元件庫以及一套完整元件庫的構成。
一、選擇開源?還是自己造輪子?
React 大環境裡面有很多優秀的 UI 元件庫,國內比較有名的 antDesign,國外的 Material-UI,都是比較穩定和優秀的元件庫。那麼我們為什麼還要自己去開發一套元件庫呢?原因大致如下:
- 有贊各個業務線 PC 產品有獨立的設計規範,包括但不限於元件樣式、互動模式。
- 有贊微商城、零售、美業等 PC 產品的業務場景較為複雜,需要深度定製某些通用的元件,如
Design
和SKU
元件。 - 需要同時支撐有贊多個業務部門的 PC 產品。
- 團隊成員以開源的模式參與元件庫的開發,期間會有很多互相的討論、碰撞,本身也是對團隊的鍛鍊過程。
二、元件庫構成
構建一個完整的元件庫需要考慮:
- 元件設計思路
- 元件程式碼規範
- 元件開發流程
- 元件測試
- 元件維護(包括 PR / issue 管理、發包、文件)
1. 元件設計思路
元件是對一些具有相同業務場景和互動模式程式碼的抽象,元件庫首先應該保證各個元件的視覺風格和互動規範保持一致,X
元件在 A
業務場景是一個互動,在 B
業務場景是另一個 UI 風格,這樣就無法對 X
進行抽象,極大的增加了元件的構建成本。所以,設計元件之初,首先需要抽象和約定一套統一的視覺風格和互動規範。
其次,元件庫的 props 定義需要具備足夠的可擴充套件性,而且元件內部完全受控,保持元件具有統一的輸入和輸出,讓我們來看一個 Button 的例子。
// Button is a react component of Zent
<Button
type="primary"
className="customer-classname"
loading={true}
disabled={false}
size="large"
onClick={this.handleClick}
>
{children}
</Button>
複製程式碼
這是一個 Button 元件,我們定義了很多標記狀態的 props,比如 type 表示 Button 的視覺風格,size 表示尺寸,disabled 禁用,loading 狀態等,這些狀態在元件內部都不會維護 state,所有的狀態由傳入的 props 來決定,自定義 className 方便我們做樣式自定義,children 方便我們自定義 Button 的顯示內容。
Button 甚至提供了a
標籤的功能,只要在Button上傳入 props:href。
// Button as <a>
<Button
type="primary"
className="customer-classname"
href="https://www.youzan.com"
target="_blank"
>
有贊首頁
</Button>
複製程式碼
我們需要做幾個約定:
- 元件所有狀態受控於 props
- 元件 children 支援自定義 Dom 結構
- 不要寫死元件內部的 Dom 結構
2. 元件程式碼規範
有贊前端內部元件庫,使用的是開源 lint 工具-- felint 。
felint 是一個整合了 eslint、stylelint、git hook 的前端程式碼檢查工具。felint 為你的專案做以下三件事:
- 初始化 eslint/stylelint 配置檔案,無論是 react 專案、vue 專案、es5 還是 es6 都提供了針對性的配置方案
- 安裝 eslint/stylelint 及其依賴到當前專案的 node_modules 裡
- 掛載 git 鉤子,在你提交程式碼時進行強制校驗
具體使用可以參考官方 doc -- felint 文件地址 。
3. 元件開發流程
約定好元件的設計思路和程式碼規範以後,接下來我們就可以參與開發元件了,元件庫的基本開發流程,包括以下幾點:
- 元件初始化
- 元件 Coding
- 元件 Demo
Zent 裡面有一個元件初始化命令:yarn new-component
,這個命令完成了元件大部分初始化工作,包括自動建立元件需要的目錄和模版程式碼,新增元件 js 和 css 程式碼。然後,我們就可以開始寫元件程式碼,程式碼風格和規範嚴格按照 lint 的規範編寫,如果不符合規範,是不能提交程式碼的。寫完元件以後,需要寫元件 Demo 並執行,方式是本地啟動 server 來執行元件 Demo,這個可以元件作為元件的除錯工具。
4. 元件測試
js 單元測試框架有很多,chai、jest、mocha、karma 等等,Zent 元件庫使用的是 jest + enzyme 的組合,下面來看一個例子:
// Button UI test
import { mount } from 'enzyme';
describe('Button', () => {
it('Button UI test', () => {
const wrapper = mount(<Button>OK</Button>);
expect(wrapper.hasClass('zent-btn')).toBe(true);
expect(wrapper.text()).to.equal('OK');
});
});
複製程式碼
使用 jest 做 UI 測試有侷限性,只能測試基本的 dom 結構 和樣式,一些邏輯互動無法測到,只能覆蓋大部分的情況。
yarn test
用來執行測試指令碼,測試結果會顯示在終端。
5. 元件維護
元件日常維護佔整個元件庫生命週期的很大一部分,元件庫做起來了以後,元件功能後續會不斷迭代,也許是 bug fix,也可能是 new feature,這些元件的迭代我們通過 PR 和 issue 來管理,同時,我們需要管理好元件的 changelog。 總的來說,元件維護主要包括:PR / issue 的處理,發包和管理 changelog。
下面以 Zent 為例,來介紹一下 PR 規範。
PR 標題規則:[ bug fix / breaking change / new feature ] 元件名字:修改內容描述
- 前面方括號用來區分 PR / issue 的型別:bug fix - 元件 bug 修復;breaking change - 不相容的改動;new feature - 新功能
- 修改內容儘可能言簡意賅,總結 PR 的改動或者描述 issue
- 描述請用中文
- 元件名字請用英文,首字母大寫
PR 用來生成 changelog,規範的 PR 有助於生成比較清晰的 changelog,一目瞭然,來看一下 Zent 的例子:
元件發包
元件發包只有擁有發包許可權的人才能操作,Zent 是以元件庫為單位發包的,yarn build
會將整個 Zent 的程式碼打包,使用命令 yarn publish
發包,在發包之前會跑元件測試,只有測試通過以後才能發包。
元件文件
一份好的 doc 是一個優秀元件庫的標準,良好的文件能夠提升元件庫的整體品質和好感度,願意花時間好好寫 doc 的團隊,那麼他們產出的元件庫應該也不會差到哪去,元件庫文件維護也是元件庫生命週期裡重要的一環,有時候你甚至需要做到中英文雙語 doc。
這裡附上 Zent 元件庫的 doc 地址:Zent。
三、小結
在本文中,我們從元件的設計思路、編碼規範、開發流程、測試、日常維護這五個方面闡述瞭如何構建一個 React 元件庫,並且以 Zent 為例講述了有贊是如何做的,任何一個元件庫都需要的經過這個生命週期,但我們需要思考的是:如何營造一個良好的元件庫生態環境? 我們需要想辦法讓更多的人蔘與其中,共同作為元件庫的維護者,選擇開源是為了給 React 生態環境做輸出,在前端元件化已經成為了既定事實的今天,我們不需要重複的造輪子,而是需要在元件化方面嘗試新的突破,脫離前端技術的束縛,站在工程師的高度去抽象自己手頭的程式碼。元件化這條路上,我們還有很多事情要做,Zent 只是一個開始。