前言
React 的開發也已經有2年時間了,先從QQ的家校羣,轉成作互動直播,主要是花樣直播這一塊。切換過來的時候,業務很是繁忙,接手過來的業務比較凌亂,也沒有任何組件複用可言。javascript
爲了提升開發效率,去年10月份也開始有意識地私下封裝一些組件,而且於今年年初在項目組裏發起了百日效率提高計劃,其中就包含組件化開發這一塊。css
本文並非要談如何去寫一個 React 組件,這一塊已經有很多精彩的文章。例如像這篇《從新設計 React 組件庫》,裏面涉及一個組件設計的各方面,如粒度控制、接口設計、數據處理等等(不排除後續也寫一篇介紹組件設計理念哈)。html
本文關鍵詞是三個,工程化、快速和可靠。咱們是但願利用工程化手段去保障快速地開發可靠的組件,工程化是手段和工具,快速和可靠,是咱們但願達到的目標。前端
前端工程化不外乎兩點,規範和自動化。java
規範
目錄與命令規範
規範,主要就是目錄規範和代碼規範。跟同事合做,通過將近20個的組件開發後,咱們大概造成了必定的目錄規範,如下是咱們大體的目錄約定。哪裏放源碼,哪裏放生產代碼,哪裏是構建工具,哪裏是例子等。有了這些的約定,往後開發和使用並一目瞭然。react
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
__tests__ -- 測試用例
|
example -- 真實demo
|
dist -- 開發者使用代碼
|
src -- 源代碼
|
config -- 項目配置
|------project.js -- 項目配置,主要被 webpack,gulp 等使用
|
|
tools -- 構建工具
|
|——————start.js -- 開發環境執行命令
|——————start.code.js -- 開發環境生成編譯後代碼命令
|
package.json
|
命令咱們也進行了規範,以下,webpack
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
// 開發環境,服務器編譯
npm start 或者 npm run dev
// 開發環境,生成代碼
npm run start.code
// 生產環境
npm run dist
// 測試
npm test
// 測試覆蓋率
npm run coverage
// 檢查你的代碼是否符合規範
npm run lint
|
代碼規範
代碼規範,主要是寫 js
,css
和 html
的規範,基本咱們都是沿用團隊以前制定好的規範,若是以前並無制定,例如 React 的 jsx 的寫法,那麼咱們就參考業界比較優秀的標準,並進行微調,例如 airbnb
的 JavaScript 規範,是不錯的參考。git
自動化
開發與發佈自動化
規範是比較人性的東西,憑着人對之的熟悉就能夠提升效率了,至於那些工做繁複的流程,單憑人的熟悉也會達到極限,那麼咱們就須要藉助自動化的工具去突破這重極限。es6
例如代碼規範,單憑人的肉眼難以識別全部不合規範的代碼,並且效率低下,藉助代碼檢測工具就可以讓人卸下這個重擔。如 css ,咱們推薦使用 stylelint ,js 則是 eslint。有這種自動化的工具協助開發者進行檢查,能更好地保障咱們的代碼質量。
自動化最爲重要的任務是,去保證開發過程良好的體驗還有發佈生產代碼。實際上,開發和發佈組件的整個過程跟平時開發一個任務很像,但卻又略有差別。
首先是開發過程當中,咱們但願一邊開發的時候,咱們開發的功能可以顯示出來,這時最好能搭建一個demo,咱們把 demo 放到了 example 目錄下,這點對 UI 組件(像toast, tips等組件) 尤其重要,邏輯組件(像ajax, utils等組件),能夠有 demo,也能夠採起測試驅動開發的方式,先制定部份測試用例,而後邊開發邊進行測試驗證。
開發過程當中的這個 demo, 跟平時開發項目基本一致,咱們就是經過配置,把 html
,js
, css
都搭建好,並且咱們是開發 React 組件,引入熱替換的功能令整個開發流程很是流暢。這裏分別是 webpack
和配合 `webpack 開發的靜態資源服務器的兩份配置: webpack & server。
可是發佈組件的這個過程跟開發項目卻又很不一樣。開發項目,咱們須要把全部的依賴都打包好,而後一併發佈。但對於組件來講,咱們只須要單獨將它的功能發佈就行了,它的相關依賴能夠在實際開發項目中引用時一併再打包。所以這裏的 package.json
寫的時候也要有所區分。跟只跟開發流程、構建、測試相關的,咱們一概放在 devDependencies
中,組件實際依賴的庫,則主要放在 dependencies
中。
鑑於咱們項目通常採用 webpack 打包,所以咱們通常只須要 es6 import 的引入方式,那咱們直接用 babel 幫咱們的項目進行生產代碼的編譯打包就能夠了,這樣能有效減小冗餘代碼。配置好 .babelrc,而後配置 package.json
的打包命令便可。要注意的是,你的組件可能含有樣式文件,配置命令的時候要記得將樣式文件也複製過去,像下面的命令,–copy-files 參數就是爲了將樣式文件直接拷貝到 dist 目錄下。
1
2
|
babel src --out-dir dist --copy-files
|
但有時候,你也想組件能兼容多種引用方式,即 umd
,那 babel
的這種打包就不夠了。這時你能夠藉助 webpack
打包 library
的能力。可參考此 配置。主要是配置 output.library
和 output.libraryTarget
。
1
2
3
4
5
6
|
output: {
// other config
library: "lib", // 表示以什麼名字輸出,這裏,會輸出爲如 exports["lib"]
libraryTarget: "umd", // 表示打包的方式
},
|
另外一點要注意的是,咱們只需打包組件的邏輯就行了,那些依賴,能夠等實際生產項目的時候再進行解析。但 webpack
默認會將依賴也打包進行,爲了不這點,你須要將這些依賴一一配置成爲 external
,這就告訴了 webpack
它們是外部引用的,能夠不用打包進來。
打包完成以後,根據指引進行 npm publish
就能夠了。這裏大致總結了一下咱們開發組件的一些流程和注意事項。
測試自動化
上述講的都跟如何提高開發效率有關的,即知足 「快速」 這個目標,對 」可靠「 有必定幫助,如穩定的流程和良好的代碼規範,但並無很是好地保證組件地穩定可靠。須要 」可靠「的組件,還須要測試來保證。
很多開發者作測試會使用 mocha
,若是是 UI 組件可能會配置上 karma
。而 React 組件測試還有一個更好的選擇,就是官方推薦的 jest + enzyme。
jest
跟 jasmine
有點相似,將一個測試庫的功能大部份集成好了(如斷言等工具),一鍵安裝 babel-jest
能夠用 es6 直接寫測試用例,搭配 jest-environment-jsdom
和 jsdom
可以模擬瀏覽器環境,結合 airbnb
寫的 react 測試庫 enzyme
, 基本能知足大部份的 React 測試需求。確實符合官方的宣傳語 painless
,這是一個無痛的測試工具。
測試邏輯組件問題倒不大,UI組件對於大部份的狀況均可以,許多事件均可以經過enzyme
模擬事件進行測試。但這裏舉的例子, react-list-scroll
組件,一個 React 的滾動列表組件,碰巧遇到一種比較難模擬的狀況,就是對 scroll
事件的模擬。這裏想展開說一下。
對於 React
的 scroll
事件而言,必需要綁定在某個元素裏才能進行模擬,不巧,對於安卓手機來講,大部份 scroll
事件都是綁定在 window
對象下的。這就很是尷尬了,須要藉助到 jsdom
的功能。經過 jest-environment-jsdom
,它可以將 jsdom
注入到 node
運行環境中,所以你能夠在測試文件中直接使用 window
對象進行模擬。例以下面代碼,模擬滾動到最底部:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
test('scroll to bottom', (done) => {
const wrapper = mount(<Wrapper />);
window.addEventListener('scroll', function(e) {
setTimeout(() => {
try {
// expect 邏輯
done();
}
catch(err) {
done.fail(err);
}
}, 100);
jest.runAllTimers();
});
let scrollTop = 768;
window.document.body.scrollTop = scrollTop; // 指明當前 scrollTop到了哪一個位置
window.dispatchEvent(new window.Event('scroll', {
scrollTop: scrollTop
}));
});
|
細心的你會發現,上圖還有一些定時器的邏輯。緣由是在組件中會有一些截流的邏輯,滾動時間隔一段時間纔去檢測滾動的位置,避免性能問題,所以加一個定時器,等待數據的返回,而 jest.runAllTimers();
則是用於告訴定時器立刻跑完。
除此以外,定時器裏還有個 try catch
的邏輯,主要是若是 expect
驗證不經過,jest
會報告錯誤,這時需用錯誤捕獲的辦法將錯誤傳給 done
(異步測試的回調),這樣才能正常退出這一個測試用例,不然會返回超時錯誤。
安卓測完了,那iPhone呢?iPhone 的 scroll
事件是綁定在具體某個元素裏的,但我這裏又不是經過 React 的 onScroll
來綁定。首先咱們得經過 window.navigator.userAgent
來區分手機類型。但因爲 userAgent
只有 getter
函數,直接設置值會報錯,所以咱們要添加一個 setter
函數給它,用這段示例代碼:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
Object.defineProperty(window.navigator, "userAgent", (function(_value){
return {
get: function _get() {
return _value;
},
set: function _set(v) {
_value = v;
}
};
})(window.navigator.userAgent));
let str = "Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1";
window.navigator.userAgent = str;
|
而後,去找到這個綁定的元素,進行事件監聽和分發就行了:
1
2
3
4
5
6
7
8
|
const wrapper = mount(<Wrapper />),
scrollComp = wrapper.find(Scroll),
scrollContainer = scrollComp.nodes[0].scrollContainer;
scrollContainer.addEventListener('scroll', function(e) { //... });
scrollContainer.dispatchEvent(// ... );
|
總結
本文主要是提取了開發組件工程化的一些關鍵要點,具體的開發腳手架能夠參考 steamer-react-component,裏面主要舉了pure-render-deepCompare-decorator 和 react-list-scroll,一個邏輯組件,一個UI組件,共兩個示例,對照着腳手架的文檔,從目錄規範、開發流程、發佈都寫得較爲清楚,你們開發組件的時候,能夠根據狀況作些調整。
若有謬誤,懇請斧正。