React.js 2016 最佳實踐

發表於2016-01-26

譯者按:近幾個月React相關話題依舊火熱,相信越來越多的開發者在嘗試這樣一項技術,我們團隊也在PC和移動端不斷總結經驗。2016來了,這應該是React走向成熟的一年,不管你是新手,還是已經對React有所瞭解,是時候總結一下最佳實踐了,讓我們看看國外的開發者總結了哪些好的實踐吧~

===========譯文分割線==============

2015可以算是React之年了,關於其版本釋出和開發者大會的話題遍佈全球。關於去年React的發展里程碑詳情,可以檢視我們整理的React 2015這一年

2016年最有趣的問題可能是,我們該如何編寫一個應用呢,有什麼推薦的庫或框架?

作為一個長時間使用React.js的開發者,我已經有自己的答案和最佳實踐了,但你可能不會同意我說的所有點。我對你的想法和意見很感興趣,請留言進行討論。

React.js logo - Best Practices for 2016

如果你只是剛開始接觸React.js,請閱讀React.js教程,或Pete Hunt的React howto

 

資料處理

在React.js應用中處理資料超級簡單的,但同時還是有些挑戰。

這是因為你可以使用多種方式,來給一個React元件傳遞屬性資料,從而構建出渲染樹。但這種方式並不總是能明顯地看出,你是否應該更新某些檢視。

2015開始湧現出一批具有更強功能和響應式解決方案的Flux庫,讓我們一起看看:

Flux

根據我們的經驗,Flux通常被過度使用了(就是大家在不需使用的場景下,還是使用了)。

Flux提供了一種清爽的方式儲存和管理應用的狀態,並在需要的時候觸發渲染。

Flux對於那些應用的全域性state(譯者注:為了對應React中的state概念,本文將不對state進行翻譯)特別有用,比如:管理登入使用者的狀態、路由狀態,或是活躍賬號狀態。如果使用臨時變數或者本地資料來處理這些狀態,會非常讓人頭疼。

我們不建議使用Flux來管理路由相關的資料,比如/items/:itemId。應該只是獲取它並存在元件的state中,這種情況下,它會在元件銷燬時一起被銷燬。

如果需要Flux的更多資訊,建議閱讀The Evolution of Flux Frameworks

使用Redux

Redux是一個JavaScript app的可預測state容器。

如果你覺得需要Flux或者相似的解決方案,你應該瞭解一下redux,並學習Dan Abramovredux入門指南,來強化你的開發技能。

Rudux發展了Flux的思想,同時降低了其複雜度。

扁平化state

API通常會返回巢狀的資源,這讓Flux或Redux架構很難處理。我們推薦使用normalizr這類庫來儘可能地扁平化state

像這樣:

(我們使用isomorphic-fetch與API進行通訊

使用immutable state

共享的可變資料是罪惡的根源——Pete Hunt, React.js Conf 2015

Immutable logo for React.js Best Practices 2016

不可變物件是指在建立後不可再被修改的物件。

不可變物件可以減少那些讓我們頭痛的工作,並且通過引用級的比對檢查來提升渲染效能。比如在shouldComponentUpdate中:

 

如何在JavaScript中實現不可變

比較麻煩的方式是,小心地編寫下面的例子,總是需要使用deep-freeze-node(在變動前進行凍結,結束後驗證結果)進行單元測試。

 

相信我,這是最明顯的例子了。

更簡單自然的方式,就是使用Immutable.js

 

Immutable.js非常快,其背後的思想也非常美妙。就算沒準備使用它,還是推薦你去看看Lee Byron的視訊Immutable Data and React,可以瞭解到它內部的實現原理。

Observables and reactive解決方案

如果你不喜歡Flux/Redux,或者想要更加reactive,不用失望!還有很多方案供你選擇,這裡是你可能需要的:

  • cycle.js(“一個更清爽的reactive框架”)
  • rx-flux(“Flux與Rxjs結合的產物”)
  • redux-rx(“Redux的Rxjs工具庫”)
  • mobservable(“可觀測的資料,reactive的功能,簡潔的程式碼”)

路由

現在幾乎所有app都有路由功能。如果你在瀏覽器中使用React.js,你將會接觸到這個點,併為其選擇一個庫。

我們選擇的是出自優秀rackt社群的react-router,這個社群總是能為React.js愛好者們帶來高質量的資源。

要使用react-router需要檢視它的文件,但更重要的是:如果你使用Flux/Redux,我們推薦你將路由state與store或全域性state保持同步

同步路由state可以讓Flux/Redux來控制路由行為,並讓元件讀取到路由資訊。

Redux的使用者可以使用redux-simple-router來省點事兒。

程式碼分割,懶載入

只有一小部分webpack的使用者知道,應用程式碼是可以分割成多個js包的。

 

這對於大型應用十分有用,因為使用者瀏覽器不用下載那些很少會使用到的程式碼,比如Profile頁。

多js包會導致額外的HTTP請求數,但對於HTTP/2的多路複用,完全不是問題。

chunk hashing 結合可以優化快取命中率。

下個版本的react-router將會對程式碼分隔做更多支援。

對於react-router的未來規劃,可以去看博文Ryan FlorenceWelcome to Future of Web Application Delivery

元件

很多人都在抱怨JSX,但首先要知道,它只是React中可選的一項能力。

最後,它們都會被Bable編譯成JavaScript。你可以繼續使用JavaScript編寫程式碼,但是在處理HTML時使用JSX會感覺更自然。特別是對於那些不懂js的人,他們可以只修改HTML相關的部分。

JSX是一個類似於XML的JavaScript擴充套件,可以配合一個簡單的語法編譯工具來使用它。——深入淺出JSX

如果你想了解更多JSX的內容,檢視文章JSX Looks Like An Abomination – But it’s Good for You

使用類

React中可以順暢地使用ES2015的Class語法。

 

我們在高階元件和mixins之間更看重前者,所以拋棄createClass更像是一個語法問題,而不是技術問題。(譯者注:在Class語法中,React元件的mixins方法將無法使用。)我們認為使用createClass和React.Component沒有對錯之分。

屬性型別(PropType)

如果你以前不檢查props的型別,那麼2016你應該開始改正了。它會幫你節省未來很多時間,相信我。

 

是的,同時也儘可能使用react-immutable-proptypes檢查Immutable.js的props。

高階元件(Higher order components)

minins將死,ES6的Class將不對其進行支援,我們需要尋找新的方法。

什麼是高階元件?

 

簡單地,你建立一個從原生元件繼承下來的元件,並且擴充套件了原始元件的行為。你可以在多種場景來使用它,比如鑑權:requireAuth({ role: ‘admin’ })(MyComponent)(檢查使用者是否在高階元件中,如果還沒有登入就進行跳轉),或者將元件與Flux/Redux的store相連通。

在RisingStack,我們也喜歡分離資料拉取和controller類的邏輯到高階元件中,這樣可以儘可能地保持view層的簡單。

測試

好的程式碼覆蓋測試是開發週期中的重要一環。幸運的是,React.js社群有很多這樣的庫來幫助我們。

元件測試

我們最喜愛的元件測試庫是AirBnb的enzyme。有了它的淺渲染特性,可以對元件的邏輯和渲染結果進行測試,非常棒對不對?它現在還不能替代selenium測試,但是將前端測試提升到了一個新高度。

 

看起來很清爽,不是嗎?

你使用chai來作為斷言庫嗎?你會喜歡chai-enyzime的。

Redux測試

測試一個reducer非常簡單,它響應actions然後將原來的state轉為新的state:

 

測試actions也很簡單,但是非同步actions就不一樣了。測試非同步的redux actions我們推薦redux-mock-store,它能幫不少忙。

 

關於更深入的redux測試,請參考官方文件。

使用npm

雖然React.js並不依賴程式碼構建工具,我們推薦WebpackBrowserify,它們都具有npm出色的能力。Npm有很多React.js的package,還可以幫助你優雅地管理依賴。

(請不要忘記複用你自己的元件,這是優化程式碼的絕佳方式。)

包大小(Bundle size)

這本身不是一個React相關的問題,但多數人都會對其React進行打包,所以我在這裡提一下。

當你對原始碼進行構建時,要保持對包大小的關注。要將其控制在最小體積,你需要思考如何require/import依賴。

檢視下面的程式碼片段,有兩種方式可以對輸出產生重大影響:

 

檢視Reduce Your bundle.js File Size By Doing This One Thing,獲取更多詳情。

我們喜歡將程式碼分隔到vendors.js和app.js,因為第三方程式碼的更新頻率比我們自己帶嗎低很多。

對輸出檔案進行hash命名(WebPack中的chunk hash),並使用長快取,我們可以顯著地減少訪問使用者需要下載的程式碼。結合程式碼懶載入,優化效果可想而知。

如果你對WebPack還很陌生,可以去看超讚的React webpack指南

元件級的hot reload

如果你曾使用livereload寫過單頁面應用,你可能知道當在處理一些與狀態相關的事情,一點程式碼儲存整個頁面就重新整理了,這種體驗有多煩人。你需要逐步點選操作到剛才的環節,然後在這樣的重複中奔潰。

在React開發中,是可以reload一個元件,同時保持它的state不變——耶,從此無需苦惱!

搭建hot reload,可參考react-transform-boilerplate

使用ES2015

前面提到過,在React.js中使用的JSX,最終會被Babel.js進行編譯。

Babel logo in React.js Best Practices 2016

Bable的能力還不止這些,它可以讓我們在瀏覽器中放心地使用ES6/ES2015。在RisingStack,我們在伺服器端和客戶端都使用了ES2015的特性,ES2015已經可以在最新的LTS Node.js版本中使用了。

程式碼檢查(Linters)

也許你已經對你的程式碼制定了程式碼規範,但是你知道React的各種程式碼規範嗎?我們建議你選擇一個程式碼規範,然後照著下面說的來做。

在RisingStack,我們強制將linters執行在持續整合(CI)系統,已經git push功能上。檢視pre-pushpre-commit

我們使用標準的JavaScript程式碼風格,並使用eslint-plugin-react來檢查React.js程式碼。

(是的,我們已經不再使用分號了)

GraphQL和Relay

GraphQL和Relay是相關的新技術。在RisingStack,我們不在生產環境使用它們,暫時保持關注。

我們寫了一個Relay的MongoDB ORM,叫做graffiti,可以使用你已有的mongoose models來建立GraphQL server。

如果你想學習這些新技術,我們建議你去看看這個庫,然後寫幾個demo玩玩。

這些React.js最佳實踐的核心點

有些優秀的技術和庫其實跟React都沒什麼關係,關鍵在於要關注社群都在做些什麼。2015這一年,React社群被Elm架構啟發了很多。

如果你知道其他2016年大家應該使用的React.js工具,請留言告訴我們。