從工程化角度討論如何快速構建可靠React組件

woshimrf 7年前發布 | 25K 次閱讀 React webpack

前言

React 的開發也已經有2年時間了,先從QQ的家校群,轉成做互動直播,主要是花樣直播這一塊。切換過來的時候,業務非常繁忙,接手過來的業務比較凌亂,也沒有任何組件復用可言。

為了提高開發效率,去年10月份也開始有意識地私下封裝一些組件,并且于今年年初在項目組里發起了百日效率提升計劃,其中就包含組件化開發這一塊。

本文并不是要談如何去寫一個 React 組件,這一塊已經有不少精彩的文章。本文關鍵詞是三個,工程化、快速和可靠。工程化是手段和工具,快速和可靠,是我們希望達到的目標。

前端工程化不外乎兩點,規范和自動化。

讀文先看此圖,能先有個大體概念:

規范

目錄與命令規范

規范,主要就是目錄規范和代碼規范。跟同事合作,經過將近20個的組件開發后,我們大概形成了一定的目錄規范,以下是我們大致的目錄約定。哪里放源碼,哪里放生產代碼,哪里是構建工具,哪里是例子等。有了這些的約定,日后開發和使用并一目了然。

__tests__ -- 測試用例
|
example -- 真實demo
|
dist -- 開發者使用代碼
|
src -- 源代碼
|
config -- 項目配置
|------project.js -- 項目配置,主要被 webpack,gulp 等使用
|      
|  
tools  -- 構建工具
|  
|——————start.js -- 開發環境執行命令
|——————start.code.js -- 開發環境生成編譯后代碼命令
|
package.json
 

命令我們也進行了規范,如下,

// 開發環境,服務器編譯
npmstart 或者 npmrundev
 
// 開發環境,生成代碼
npmrunstart.code
 
// 生產環境
npmrundist
 
// 測試
npmtest
 
// 測試覆蓋率
npmruncoverage
 
// 檢查你的代碼是否符合規范
npmrunlint
 
 

代碼規范

代碼規范,主要是寫 js , css 和 html 的規范,基本我們都是沿用團隊之前制定好的規范,如果之前并沒有制定,例如 React 的 jsx 的寫法,那么我們就參考業界比較優秀的標準,并進行微調,例如 airbnb 的 JavaScript 規范,是不錯的參考。

自動化

開發與發布自動化

規范是比較人性的東西,憑著人對之的熟悉就可以提高效率了,至于那些工作繁復的流程,單憑人的熟悉也會達到極限,那么我們就需要借助自動化的工具去突破這重極限。

例如代碼規范,單憑人的肉眼難以識別所有不合規范的代碼,而且效率低下,借助代碼檢測工具就可讓人卸下這個重擔。如 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 目錄下。

babelsrc --out-dirdist --copy-files
 

但有時候,你也想組件能兼容多種引用方式,即 umd ,那 babel 的這種打包就不夠了。這時你可以借助 webpack 打包 library 的能力。可參考此 配置 。主要是配置 output.library 和 output.libraryTarget 。

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 對象進行模擬。例如下面代碼,模擬滾動到最底部:

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();
 
    });
 
    letscrollTop = 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 函數給它,用這段示例代碼:

Object.defineProperty(window.navigator, "userAgent", (function(_value){
  return {
    get: function _get() {
      return _value;
    },
    set: function _set(v) {
        _value = v;
    }
  };
})(window.navigator.userAgent));
 
letstr = "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;
 

然后,去找到這個綁定的元素,進行事件監聽和分發就好了:

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組件,共兩個示例,對照著腳手架的文檔,從目錄規范、開發流程、發布都寫得較為清楚,大家開發組件的時候,可以根據情況做些調整。

 

 

來自:http://www.alloyteam.com/2017/03/from-an-engineering-point-of-view-discusses-how-to-construct-reliable-components-react/

 

 本文由用戶 woshimrf 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!