在上篇《從新設計 React 組件庫》中咱們從宏觀層面一塊兒探討告終構自由且數據解耦的 React 組件庫應當如何設計,在本文中讓咱們從具體實踐的角度來看如何將這樣的設計落地。css
在傳統的組件庫設計中,組件分類一直都不是一個必選項,大多數人都認爲一個組件到底是屬於組件類仍是控件類,不過是名字上的不一樣而已,並無實際意義。但在將組件代碼寫法區分爲純函數與 ES6 class 兩種以後,咱們發現組件的寫法同時也表明着組件的類型,這時就能夠給予不一樣組件一個更清晰的定義,分別是:前端
在進行了這樣清晰的分類以後,每當咱們須要新增一個組件時,咱們均可以從是否含有內部狀態,是否有交互等幾個方面來將其歸入組件或控件,並以此來肯定其相應的代碼規範。webpack
延伸來講,除了基礎的組件與控件的區別以外,咱們還推薦你們從業務的角度出發再劃分出一種新的組件類型,即容器。git
舉例來講,在 Material Design 大行其道的今天,應該不會有人對卡片這樣一種基礎的內容展現形式感到陌生。對應到前端組件庫中,做爲展現內容的骨架,卡片自己應當是一個純渲染組件,但在將其帶入具體的業務場景中後就會發現,卡片自己實際上是有狀態的,常見的如數據加載中、數據爲空、數據錯誤等。這樣一個無交互但含有自身狀態的組件不管歸於上述的哪一個分類都會讓人感到奇怪,因此咱們又引入了容器這樣一個新的分類,專門用來存放卡片這類組件。看到這裏,相信聰明的你應該能體會到組件分類的真正意義了,那就是用組件分類這樣一種形式來強迫工程師去思考每個組件的本質,而後再利用 pure render 等方法去優化組件性能。做爲離用戶最近的一批工程師,前端工程師所應該關心的,除了代碼自己以外,用戶體驗,人機交互等領域方面的經驗與知識,也是判斷前端工程師是否優秀的另外一把標尺。github
另外一方面來說,咱們又能夠從容器組件延伸出強依賴數據的組件應當如何設計這樣一個更加抽象的問題。從組件庫設計的角度來說,正如上一篇文章中所提到的,不建議將數據獲取等邏輯放在組件裏去作的。但結合業務場景來講,統一數據獲取等邏輯確實是提高業務開發效率的不二選擇,這方面的具體實踐你們能夠參考瓊玖以前的文章《React實踐 - Component Generator》。簡而言之,使用高階組件在這裏是一個不錯的選擇。web
回到代碼自己,拋開純函數組件不談,咱們這裏再來討論一個編寫智能組件時常常會踩到的坑。npm
在 React 的生命週期函數中,有一個功能十分強大的函數,那就是 componentWillReceiveProps
,在這個函數中,咱們既能夠拿到 this.props
又能夠拿到 nextProps
,因此理論上來說,咱們能夠在這裏利用這些數據對組件作任何邏輯上的變動。另外一方面,智能組件通常須要支持木偶與智能兩種調用方式,以方便使用者在使用時根據是否須要在業務代碼中保存組件狀態使用。木偶組件標配的 props 通常爲 value 加一個回調函數 onChange,這時組件自己就只須要負責根據接收到的 props 進行渲染。而智能組件的標配 props 通常只須要設置一個 defaultValue,也就是外部只負責定義組件的初始狀態,接下來組件本身會根據交互來改變內部狀態。這裏咱們能夠經過在 componentWillReceiveProps
中同步 props 到 state 的方式來支持兩種不一樣的調用方式,即若是外部直接改變了 value
值,那麼就將新的 value
值同步到組件內部的 state 上,若是外部沒有改變 value
值,那麼就交由組件內部的 state 全權負責組件狀態的更新。數組
constructor(props) {
super(props);
this.state = {
value: props.defaultValue,
};
}
componentWillReceiveProps(nextProps) {
// sync state to props
if (this.props.value !== nextProps.value) {
this.setState({
value: nextProps.value,
});
}
}
handleChange(value) {
this.setState({
value,
});
this.props.onChange(value);
}
render() {
const { value } = this.state;
return <input value={value} onChange={::this.handleChange} />;
}複製代碼
編寫組件庫自己並非最終目的,讓更多的人在業務開發中使用起來纔是。組件庫做爲一個自身封裝程度較高,內聚性較強的技術項目,開發文檔是否足夠清晰,完善,也是決定項目成敗的另外一個關鍵因素。bash
優秀的組件庫文檔起碼要知足如下兩個要求:babel
屬性全覆蓋的重要性在這裏再也不贅述,使用者在不閱讀源碼的前提下想要了解組件的全部功能,閱讀組件文檔是惟一的途徑。
另外一方面,因爲 React 組件自己是高度可定製的,因此若是開發者不可以提供具體的示例,使用者在使用組件進行一個複雜業務開發時就將由於缺乏指導而變得異常痛苦。從代碼質量管控的角度來說,豐富的示例也是對組件單元測試的一次具象。在將來維護組件增長新功能時,示例豐富的好處就將體現得淋漓盡致:當組件新增了一些邏輯後,原先全部的示例都仍能完美運行時,咱們也會對新加的這個功能更有信心並避免 regression 的發生。
做爲業務項目的基礎依賴,組件庫通常都須要打包發佈至 npm 以方便業務項目使用。在對組件庫進行打包時,爲了方便業務項目在具體業務場景下的使用,組件庫須要支持如下兩種打包方式。
第一種打包方式是使用 webpack
將全部組件打包成一個文件至 dist/
文件夾中:
dist/xui.js
dist/xui.css
複製代碼
在業務項目中能夠經過
import { XXX } from 'xui';
複製代碼
的方式直接調用相應組件。
另外一種打包方式是使用 babel
將每一個組件都分別編譯至對應的 lib/
文件夾中,並分別編譯每一個組件的 CSS
文件:
lib/carousel/index.js
lib/carousel/index.css
lib/input/index.js
lib/input/index.css
...
複製代碼
在業務項目中能夠經過
import Carousel from 'xui/lib/carousel';
import 'xui/lib/carousel/index.css';
複製代碼
的方式按需調用組件。
在本文中,咱們主要從組件分類、文檔管理、打包發佈三個方面闡述瞭如何將結構自由且數據解耦的 React 組件庫落到實處。
在下一篇文章中,咱們將與你們分享組件庫國際化方案,敬請期待。