React.js 2016 最佳實踐 徬梓閱讀 1584收藏 71

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

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

2015能夠算是React之年了,關於其版本發佈和開發者大會的話題遍及全球。關於去年React的發展里程碑詳情,能夠查看咱們整理的React 2015這一年前端

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

做爲一個長時間使用React.js的開發者,我已經有本身的答案和最佳實踐了,但你可能不會贊成我說的全部點。我對你的想法和意見很感興趣,請留言進行討論。node

React.js logo - Best Practices for 2016

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

數據處理

在React.js應用中處理數據超級簡單的,但同時仍是有些挑戰。webpack

這是由於你可使用多種方式,來給一個React組件傳遞屬性數據,從而構建出渲染樹。但這種方式並不老是能明顯地看出,你是否應該更新某些視圖。git

2015開始涌現出一批具備更強功能和響應式解決方案的Flux庫,讓咱們一塊兒看看:github

Flux

根據咱們的經驗,Flux一般被過分使用了(就是你們在不需使用的場景下,仍是使用了)。web

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

像這樣:

const data = normalize(response, arrayOf(schema.user))
 
state = _.merge(state, data.entities)

       

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

使用immutable state

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

Immutable logo for React.js Best Practices 2016

不可變對象是指在建立後不可再被修改的對象。

不可變對象能夠減小那些讓咱們頭痛的工做,而且經過引用級的比對檢查來提高渲染性能。好比在shouldComponentUpdate中:

shouldComponentUpdate(nexProps) {  
 // 不進行對象的深度對比
 return this.props.immutableFoo !== nexProps.immutableFoo
}

       

如何在JavaScript中實現不可變

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

return {  
  ...state,
  foo
}
 
return arr1.concat(arr2)

       

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

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

import { fromJS } from 'immutable'
 
const state = fromJS({ bar: 'biz' })  
const newState = foo.set('bar', 'baz')

       

Immutable.js很是快,其背後的思想也很是美妙。就算沒準備使用它,仍是推薦你去看看Lee Byron的視頻Immutable        Data and React,能夠了解到它內部的實現原理。

Observables and reactive解決方案

若是你不喜歡Flux/Redux,或者想要更加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包的。

require.ensure([], () => {  
  const Profile = require('./Profile.js')
  this.setState({
    currentComponent: Profile
  })
})

       

這對於大型應用十分有用,由於用戶瀏覽器不用下載那些不多會使用到的代碼,好比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語法。

class HelloMessage extends React.Component {  
  render() {
    return <div>Hello {this.props.name}</div>
  }
}

       

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

屬性類型(PropType)

若是你之前不檢查props的類型,那麼2016你應該開始改正了。它會幫你節省將來不少時間,相信我。

<coed>MyComponent.propTypes = {  
  isLoading: PropTypes.bool.isRequired,
  items: ImmutablePropTypes.listOf(
    ImmutablePropTypes.contains({
      name: PropTypes.string.isRequired,
    })
  ).isRequired
}</coed>

       

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

高階組件(Higher order components)

minins將死,ES6的Class將不對其進行支持,咱們須要尋找新的方法。

什麼是高階組件?

PassData({ foo: 'bar' })(MyComponent)

       

簡單地,你建立一個從原生組件繼承下來的組件,而且擴展了原始組件的行爲。你能夠在多種場景來使用它,好比鑑權:requireAuth({        role: 'admin'        })(MyComponent)(檢查用戶是否在高級組件中,若是尚未登陸就進行跳轉),或者將組件與Flux/Redux的store相連通。

在RisingStack,咱們也喜歡分離數據拉取和controller類的邏輯到高階組件中,這樣能夠儘量地保持view層的簡單。

測試

好的代碼覆蓋測試是開發週期中的重要一環。幸運的是,React.js社區有不少這樣的庫來幫助咱們。

組件測試

咱們最喜好的組件測試庫是AirBnb的enzyme。有了它的淺渲染特性,能夠對組件的邏輯和渲染結果進行測試,很是棒對不對?它如今還不能替代selenium測試,可是將前端測試提高到了一個新高度。

it('simulates click events', () => {  
  const onButtonClick = sinon.spy()
  const wrapper = shallow(
    <Foo onButtonClick={onButtonClick} />
  )
  wrapper.find('button').simulate('click')
  expect(onButtonClick.calledOnce).to.be.true
})

       

看起來很清爽,不是嗎?

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

Redux測試

測試一個reducer很是簡單,它響應actions而後將原來的state轉爲新的state:

it('should set token', () => {  
  const nextState = reducer(undefined, {
    type: USER_SET_TOKEN,
    token: 'my-token'
  })
 
  // immutable.js state output
  expect(nextState.toJS()).to.be.eql({
    token: 'my-token'
  })
})

       

測試actions也很簡單,可是異步actions就不同了。測試異步的redux        actions咱們推薦redux-mock-store,它能幫很多忙。

it('should dispatch action', (done) => {    const getState = {}  const action = { type: 'ADD_TODO' }  const expectedActions = [action]  const store = mockStore(getState, expectedActions, done)  store.dispatch(action) })        

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

使用npm

雖然React.js並不依賴代碼構建工具,咱們推薦WebpackBrowserify,它們都具備npm出色的能力。Npm有不少React.js的package,還能夠幫助你優雅地管理依賴。

(請不要忘記複用你本身的組件,這是優化代碼的絕佳方式。)

包大小(Bundle size)

這自己不是一個React相關的問題,但多數人都會對其React進行打包,因此我在這裏提一下。

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

查看下面的代碼片斷,有兩種方式能夠對輸出產生重大影響:

import { concat, sortBy, map, sample } from 'lodash'
 
// vs.
import concat from 'lodash/concat';  
import sortBy from 'lodash/sortBy';  
import map from 'lodash/map';  
import sample from 'lodash/sample';

       

查看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工具,請留言告訴咱們。

相關文章
相關標籤/搜索