React.js 最佳實踐(2016)_連接修正版

譯者按:最近React(web/native)依舊如火如荼,相信你們都躍躍欲試,咱們團隊也開始在React領域有所嘗試. 2016年應該是
React 逐漸走向成熟的一年,讓咱們一塊兒來看看國外的開發者們都總結了哪些"最佳實踐".javascript

2015年 React 在全世界都有不少關於新的更新和開發者大會的討論.關於去年的重要事件,請參考 React in 2015.
那麼,2016年最有趣的問題來了:咱們應該如何開發一個 App, 有什麼推薦的庫?
做爲一名長期使用 React.js 的開發者,我對此問題有本身的答案和最佳實踐,但你可能不必定徹底贊成.我對你的想法和觀點頗有興趣,請留言以便討論.html

圖片描述

若是你剛剛開始接觸React.js,能夠查看咱們的 React.js 教程,或者 Pete Hunt 的 React howto.前端

數據處理

在 React.js 應用中處理數據很是簡單,但也充滿挑戰.
這是由於你可使用多種方式將屬性數據傳遞給 React 組件,從而構建出渲染樹,但你應該怎樣更新視圖卻不是顯而易見的.
2015一開始便誕生了不少不一樣 Flux 庫,隨後涌現出出更多具備更強功能和更加響應式解決方案.
讓咱們一塊兒來看看:java

Flux

根據咱們的經驗,Flux 常常被過分使用,(就是你們老是在不須要它的時候仍然用了它).
Flux 提供了一種很是清晰的方式來存儲和更新App 全局 state(譯者注:對應 react 中的 state),並在須要的時候觸發渲染.
Flux 在管理App的全局狀態時頗有用,好比:管理已登陸用戶狀態,路由狀態,或者是活躍帳號狀態,但如果用來管理臨時數據或者本地數據,馬上就會變得很痛苦.
咱們不推薦使用 Flux 來管理路由相關的數據,好比 /items/:itemId.獲取路由數據並存儲在組件的 state 之中.這種狀況下,它會在組件銷燬時一塊兒被銷燬.
若是你想了解更多關於 Flux 的信息,建議閱讀 The Evolution of Flux Frameworks.node

使用 Redux

Redux 是一個 JavaScript App的可預測 state 容器.
若是你以爲須要 Flux 或者類似的解決方案,你應該瞭解一下 redux,並學習 Dan AbramovGetting started with redux,這可以迅速提升你的開發技能.
Redux 延續並改進了 Flux 的思想,學習了 Elm ,避開了 Flux 的複雜度(譯者注:Elm 是一門函數式編程語言).react

1.扁平化 statewebpack

API 常常會返回嵌套的資源.這在 Flux 或基於 Redux 的架構中處理起來會很是困難.咱們推薦使用 normalizr 這類庫將數據進行扁平化處理,儘量地扁平化state.
像這樣:git

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

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

2.使用 immutable stateweb

共享的可變性 state 是罪惡的根源. - Pete Hunt, React.js Conf 2015
圖片描述
不可變對象是指在建立後不可再被修改的對象.
不可變對象可讓咱們免於痛苦,而且經過引用級的比對檢查來提高渲染性能.好比在 shouldComponentUpdate 中:

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

3. 如何在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.它很是深入的講解了 Immutable.js 的工做原理.

4. Observables and Reactive 解決方案

若是你不喜歡 Flux/Redux 或者只是想要更加 reactive,不用失望!還有不少數據處理的方案供你選擇,這裏有一個多是你想要的庫的簡單列表:

  • cycle.js(「一個更清晰簡潔的函數式 reactive JavaScript 框架」)

  • rx-flux(「Flux 架構與 Rxjs 的結合」)

  • redux-rx(「Redux的 Rxjs 工具集」)

  • mobservable(「可預測的數據,reactive的功能,簡潔的代碼」)

路由

幾乎全部 App 都有路由功能.若是你在瀏覽器中使用 React.js,你將會在挑選庫的時候遇到選擇性問題.
咱們的選擇是react-router, 來自優秀的 rackt 社區.Racket 給 React.js 愛好者們帶來了不少高質量資源.
要使用 react-router,請查看它的文檔.但更重要的是:若是你使用Flux/Redux,咱們推薦你將路由 state 與 store 或全局 state 保持同步.
同步的路由 state 會幫助你控制 Flux/Redux Actions 的路由行爲,並能在組件中讀取路由狀態和參數.
Redux 用戶能夠經過 redux-simple-router 這個庫輕鬆實現它.

代碼分割,惰性加載

只有一小部分 webpack 用戶知 App 代碼能夠分割成多個 JavaScript 塊.

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

這對於大型應用十分有用,每次部署以後用戶瀏覽器不用下載那些不多會使用到的代碼,好比Profile頁面. 更多代碼塊將致使更多 HTTP 請求 - 可是使用 HTTP/2 多路複用就沒有問題.
結合 chunk hashing,能夠在代碼更新以後優化緩存命中率.
下個版本的 react-router 將會對代碼分隔作更多支持.
對於 react-router 的將來規劃,能夠去查看博客 Ryan Florence: Welcome to Future of Web Application Delivery.

組件

不少人都在抱怨JSX,但首先要知道,它在 React 中是可選的.
JSX 在最後都會經過 Babel 被編譯成 JavaScript.你能夠直接編寫 JavaScript 來替代 JSX,可是在處理 HTML 的時候使用 JSX 會感受更加天然.
特別是對於不懂技術的人來講,他們只能夠理解和修改必要的部分.

JSX 是一種與 XML 相似的 JavaScript 語法擴展.你能夠經過一個簡單的 JSX 語法轉換器來轉換它.— JSX in depth

若是你想了解更多 JSX 的內容,查看文章 JSX Looks Like An Abomination – But it’s Good for You

使用 Classes

React 與 ES2015 的 Class 語法搭配的很好.

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

相對於mixins,咱們更喜歡高階組件,因此保留 createClass 更像是一個語法問題,而不是技術問題. 咱們認爲使用 createClass 或者 React.Component 只是選擇不一樣而已,沒有對錯之分.

屬性類型

若是你仍然沒有檢查 熟悉類型,那麼你應該從2016年開始作起,這將爲你節省大量的時間,相信我.

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

固然,也可使用 react-immutable-proptypes 驗證 Immutable.js 所編寫的屬性.

高階組件

當前 mixins 將死,並且在 ES6 的 Class 再也不支持 mixins,咱們應當尋找新方案.

什麼是高階組件?

PassData({ foo: 'bar' })(MyComponent)
簡單來說,從由原始組件創造一個新的組件而且擴展它的行爲.你能夠在多種場景來使用它,好比鑑權:requireAuth({ role: 'admin' })(MyComponent)(檢查用戶權限,若是未登陸就跳轉),或者將組件與 Flux/Redux 的 store 連通.
在 RisingStack,咱們也喜歡將數據拉取和控制類的邏輯分離到高階組件中,以儘量地保持 view 層的簡單.

測試

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

組件測試

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

it('simulates click events', () => {  
  const onButtonClick = sinon.spy()
  const wrapper = shallow(
    
  )
  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 就不太同樣了.對於測試異步的 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 並不依賴代碼打包工具就能夠工做得很好,但咱們仍是推薦使用 Webpack 或者 Browserify 來發揮 npm 的能力.Npm 有不少 React.js 的包,能夠幫助你優雅地管理依賴.
(請不要忘記複用你本身的組件,這是優化代碼的絕佳方式.)

Bundle 大小

這自己不是一個 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 兩個文件,由於 vendors 相對於咱們的代碼庫來講更新頻率低不少.
對輸出文件進行 hash 命名(WebPack中的chunk hash),並使用長緩存,咱們能夠顯著地減小訪問用戶須要下載的代碼.結合代碼懶加載,優化效果很是顯著.
若是你還不太熟悉 Webpack,能夠查看這本優秀的 React webpack cookbook.

組件級別的 hot reload

若是你曾使用過hot reload編寫單頁面應用,當你在處理某些與狀態相關的事情時,可能你就會明白當你在編輯器中點擊保存,整個頁面就從新加載了是多麼使人討厭.你須要逐步點擊操做到剛纔的環節,而後在這樣的重複中奔潰.
經過 React,在重載組件的同時保持組件狀態已經成爲可能,今後再也不痛苦!
關於如何搭建hot reload,可參考 react-transform-boilerplate.

使用ES2015

前面有提到過,咱們在 React.js 組件中使用 JSX,而後使用 Babel.js 進行編譯.
圖片描述
Babel 的能力遠不止這些,它也可讓咱們如今就能夠給瀏覽器編寫 ES6/ES2015 代碼.在RisingStack,咱們在服務器端和客戶端都使用了ES2015的特性,ES2015已經能夠在最新的LTS Node.js版本中使用了.

代碼檢查

或許你已經給你的 JavaScript 代碼制定了代碼規範,可是你知道也有用於 React 的代碼規範了嗎?咱們建議你選擇一個代碼規範,而後照着它說的來作.
在 RisingStack,咱們也將 linters 強制運行在 CI 系統上,git push 亦然.能夠試試 pre-push 或者 pre-commit.
咱們使用標準的 JavaScript 代碼風格,並使用 eslint-plugin-react 來檢查React.js代碼.
(是的,咱們已經再也不使用分號了)

GraphQL 和 Relay

相對而言 GraphQL 和 Relay 還屬於新技術,在 RisingStack,咱們尚未在產品環境中使用它們,但保持關注.
咱們寫過一個 Relay 的 MongoDB ORM 庫,叫作 graffiti,可使用你已有的 mongoose models 來建立 GraphQL server.
若是你想要學習這些新技術,咱們建議你能夠找來玩一玩.

盡情享用這些 React.js 的最佳實踐

有些優秀的技術和庫其實跟React都幾乎不要緊,但要關注社區的其餘人都在作些什麼.2015這一年,React社區被 Elm 架構啓發了不少.
若是你知道其它在2016年必要的 React.js 工具,請在下面給咱們留言!


原文做者:Peter Marton,RisingStack技術總監
原文連接:https://blog.risingstack.com/...
翻譯自力譜宿雲LeapCloud團隊_UX組成員:Jason

關於MaxLeap
MaxLeap移動雲服務平臺爲企業提供一站式的移動研發和運營雲服務,幫助企業快速研發和上線移動應用,平臺提供數據雲存儲,雲引擎,支付管理,IM,數據分析和營銷自動化等服務。
MaxLeap官網連接: https://maxleap.cn歡迎關注微信公衆號:MaxLeap_yidongyanfa

相關文章
相關標籤/搜索