react 有哪些最佳實踐?

截至到目前,react作為前端最火的框架之一,有哪些最佳實踐?


React.js 作為前端框架的後起之秀,卻在2015年攜著虛擬 DOM,組件化,單向數據流等利器,給前端 UI 構建掀起了一波聲勢浩大的函數式新潮流。新鮮出爐的一篇 React.js 最佳實踐,基本涵蓋了所有的 React.js 生態周邊,可用於實踐參考。文章不僅表明了 Flux 經常被濫用的觀點,也推薦開發者使用 Redux 作為 JavaScript 的可預測狀態容器,並且提出保持狀態扁平化和使用 Immutable.js 等數據處理解決方案。與此同時,也從高階組件,組件測試以及組件級別熱重載等方面提供了建議,當然也涉及了 Webpack,HTTP 2,使用 ES2015 乃至 Linters 等代碼層面的建議。

不得不吐槽知乎編輯器的渣渣功能,更佳閱讀效果,可以到我的博客看雙語版:【譯】展望2016,React.js 最佳實踐 (中英對照版)

------

【譯】展望2016,React.js 最佳實踐

原文地址:&

過去的2015年,React 在全世界範圍都是一派欣欣向榮的景象,開發者會議無一不熱衷於這個話題。在過去一年中發生了很多重要的里程碑事件,更多詳情可以查看我們關於 React in 2015 的總結。

在新的2016年里,最有趣的問題來了:我們該如何開發一個應用,有什麼推薦使用的庫?

作為一名長時間使用 React.js 的開發者來說,我對這個問題有自己的答案以及最佳實踐,但也有可能你不會完全認同。我也非常樂於傾聽你的想法和觀點:請留言以便討論。

如果你才剛剛開始學習 React.js,可以查看我們的 React.js 教程,或者 Pete Hunt 所寫的 React howto React ecosystem.)。

數據處理

在 React.js 應用中處理數據輕而易舉,與此同時亦充滿挑戰。這是因為你可以通過各種方式將屬性數據傳遞給 React 組件,並從中構建渲染樹;然而這種方式也並非那麼顯而易見,到底該如何更新視圖。2015之初誕生了很多不同 Flux 庫,並不斷產出了更加實用的響應式方案。

讓我們看看現在的情況:

Flux

根據我們的經驗,Flux 經常被濫用,(這意味著大家總是在不需要的時候就用上它)。

Flux 提供了一種非常清晰的方式來存儲和更新應用狀態,並且只會在必要的時候才觸發頁面渲染。

Flux 致力於應用的全局狀態管理,比如:管理已登錄用戶狀態,路由狀態,或者是活躍賬戶狀態,但若是用來管理臨時數據或者本地數據,瞬間就變成了痛苦。

我們不推薦使用 Flux 來管理路由相關的數據,比如 /items/:itemId。而只是獲取路由數據並存儲在組件的 state 之中。在這種情況下,它會在組件消失之後一起被銷毀。

如果你想了解更多關於 Flux 的信息,The Evolution of Flux Frameworks 非常值得一讀。

使用 Redux

Redux 是一個 JavaScript 應用的可預測狀態容器。

如果你覺得需要 Flux 或者一種類似的解決方案,你應該了解一下 redux,以及學習 Dan Abramov 的Getting started with redux 課程,這能夠迅速提高你的開發技能。

Redux 延續並改進了 Flux 的思想,並從 Elm 架構中取經,規避了 Flux 的複雜度。(譯者註:Elm 是一門面向 Web 的函數式編程語言,致力於改善客戶端 Web 編程體驗。)

保持狀態扁平化

API 經常會返回嵌套資源。這在 Flux 或基於 Redux 的架構中處理起來會非常困難。我們推薦使用 normalizr 之類的庫將數據進行扁平化處理,保持狀態儘可能地扁平化

示意:

const data = normalize(response, arrayOf(schema.user))

state = _.merge(state, data.entities)

(我們使用 isomorphic-fetch 來與 APIs 進行交互)

使用 immutable 狀態

共享的可變性狀態乃萬惡之源。 —— Pete Hunt, React.js Conf 2015

不可變對象是一種在創建之後就不可修改的對象。不可變對象可以讓我們免於痛楚,並通過引用級別的比對檢查來改善渲染性能 。比如說在 shouldComponentUpdate 中:

shouldComponentUpdate(nexProps) {
// instead of object deep comparsion
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。視頻對於 Immutable.js 的工作原理有著非常深刻的講解。

觀察式與響應式方案

如果你不喜歡 Flux/Redux 或者只是想要更加 reactive,不要失望!這兒有很多其他數據處理的解決方案。這就有一個相關庫的簡要列表供你參考:

  • cycle.js(「A functional and reactive JavaScript framework for cleaner code」)
  • rx-flux(「The Flux architecture with RxJS」)
  • redux-rx(「RxJS utilities for Redux.」)
  • mobservable(「Observable data. Reactive functions. Simple code.」)

路由

幾乎所有的客戶端應用都或多或少需要使用路由。如果你在瀏覽器中使用 React.js,你就會在挑選庫的時候碰到這個分歧點。我們的選擇是出自優秀的 rackt 社區的 react-router。Racket 給 React.js 的擁簇者帶來了很多高質量資源。

你可以查看他們的文檔以便於集成 react-router,但是更重要的是:如果你使用 Flux/Redux,我們建議你將路由狀態和你的 store 或全局狀態保持同步

同步的路由狀態可以幫助你對 Flux/Redux 的 Actions 所提供的路由行為有所控制,並且能夠在組件中讀取路由狀態和參數。

Redux 用戶可以通過 redux-simple-router 這個庫輕鬆實現它。

代碼分割,惰性載入

只有一小部分 webpack 用戶知道應用代碼是可以分割的,將 bundler 的輸出拆分成多個 JavaScript 塊:

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

這在大型應用中會非常有用,因為在每次部署之後,用戶瀏覽器就沒有必要下載那些很少用到的代碼,比如 profile 頁面。

更多代碼塊將導致更多 HTTP 請求 —— 但是使用 HTTP/2 multiplexed 的話就不成問題。結合 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 語法轉換器來編譯 React。 —— JSX in depth

如果你想要了解更多關於 JSX 的信息,可以查看 JSX Looks Like An Abomination - But it』s Good for You 這篇文章。

使用 Class

React 和 ES2015 的 Class 語法搭配完美。

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

我們喜歡高階組件更勝於 mixins,所以對於我們來說,保留 createClass 就更像一個語法問題而不是技術問題。我們相信使用 createClass 而不是 React.Component 絕對無可厚非,反之亦然。

屬性類型

如果你在2016年依然沒有檢查 properties,那麼你應該從現在開始做起,這將為你節省大量時間,相信我。

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

當然,驗證 Immutable.js 所編寫的 properties 也是可能的,可以使用react-immutable-proptypes。

高階組件

目前來說,mixins 已死,而且在 ES6 Class 組件中已經不再被支持,我們應當尋找不同的替代方案。

那什麼是高階組件呢?

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

本質上來說,你可以由原始組件創造一個新的組件並且擴展它的行為。你可以在多種情況下使用它,比如授權:requireAuth({ role: "admin" })(MyComponent) (檢查上層組件中的用戶,若是未登錄則需要重定向),或者是連接你的組件和 Flux/Redux 倉庫。

在RisingStack,我們也將數據獲取和類似 Controller 的邏輯分割成高階組件,並保持視圖層儘可能簡單。

測試

在開發周期中,維持測試的高覆蓋率是非常重要的一部分。幸運的是, 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,並且將原來的狀態進行更新:

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 並不依賴代碼打包工具就可以很好地工作,但我們還是推薦使用 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 相對於我們的代碼庫來說更新不是那麼頻繁。

將輸出文件名稱進行哈希化處理 (Webpack 中的 chunk hash),並使用長緩存,我們可以大大減少用戶需要下載的代碼大小。結合惰性載入,優化效果可想而知。

如果你還不太熟悉 Webpack,可以查看這本優秀的 React webpack 手冊。

組件級別熱重載

如果你曾經使用過熱載入來編寫單頁面應用,當你在處理某些與狀態相關的事情時,可能你就會明白當你在編輯器中點擊保存,整個頁面就重新載入了是多麼令人討厭。這樣子就不得不重新點擊一遍應用,重複如此會令人抓狂的。

通過 React,在重載組件的同時保持組件狀態已經成為可能 —— 耶,從此不再痛苦!(沒有蛀牙!)

關於如何搭建熱重載,可以參考 react-transform-boilerplate。

使用ES2015

前面有提到過,我們可以在 React.js 組件中使用 JSX,然後使用Babel.js進行編譯。

其實 Babel 的能力遠不止如此,它也可以讓我們現在就可以給瀏覽器編寫 ES6/ES2015 代碼。在 RisingStack,我們在伺服器端和客戶端都使用了 ES2015 特性,這都已經在最新的 LTS Node.js 版本中被實現了。

Linters

或許你已經給你的 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 模型直接創建一個 GraphQL 伺服器。

如果你想要學習這些新技術,我們建議你可以找來玩一玩。

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

有些突出的技術和庫其實跟 React.js 並不相關 —— 但是保持視野開闊,關注社區的其他人都在做些什麼。React 社區在2015年里就受到了 Elm 架構 的很多啟發。

如果你知道其它在2016年必不可少的 React.js 工具,請留言讓我們知道!

-------

原作者: Péter Márton

CTO at RisingStack, brewing beer with Node.js


應該去仔細讀它的官方文檔,關鍵的實踐指導已經說了,如:

如果你關注的是用browserfy還是webpack,就恕不回答了。


React作為時下最熱的前端框架,各位有什麼經驗分享下嗎? - eisneim 的回答


原文鏈接: http://click.aliyun.com/m/9463/

現在最熱門的前端框架,毫無疑問是 React 。

上周,基於 React 的 React Native 發布,結果一天之內,就獲得了 5000 顆星,受矚目程度可見一斑。

React 起源於 Facebook 的內部項目,因為該公司對市場上所有 JavaScript MVC 框架,都不滿意,就決定自己寫一套,用來架設 Instagram 的網站。做出來以後,發現這套東西很好用,就在2013年5月開源了。

由於 React 的設計思想極其獨特,屬於革命性創新,性能出眾,代碼邏輯卻非常簡單。所以,越來越多的人開始關注和使用,認為它可能是將來 Web 開發的主流工具。

這個項目本身也越滾越大,從最早的UI引擎變成了一整套前後端通吃的 Web App 解決方案。衍生的 React Native 項目,目標更是宏偉,希望用寫 Web App 的方式去寫 Native App。如果能夠實現,整個互聯網行業都會被顛覆,因為同一組人只需要寫一次 UI ,就能同時運行在伺服器、瀏覽器和手機(參見《也許,DOM 不是答案》)。

既然 React 這麼熱門,看上去充滿希望,當然應該好好學一下。從技術角度,可以滿足好奇心,提高技術水平;從職業角度,有利於求職和晉陞,有利於參與潛力大的項目。但是,好的 React 教程卻不容易找到,這一方面因為這項技術太新,剛剛開始走紅,大家都沒有經驗,還在摸索之中;另一方面因為 React 本身還在不斷變動,API 一直在調整,至今沒發布1.0版。

我學習 React 時,就很苦惱。有的教程討論一些細節問題,對入門沒幫助;有的教程寫得不錯,但比較短,無助於看清全貌。我斷斷續續學了幾個月,看過二十幾篇教程,在這個過程中,將對自己有幫助的 Demo 都收集下來,做成了一個庫React Demos 。

下面,我就根據這個庫,寫一篇全面又易懂的 React 入門教程。你只需要跟著每一個 Demo 做一遍,就能初步掌握 React 。當然,前提是你必須擁有基本 JavaScript 和 DOM 知識,但是你讀完就會發現,React 所要求的預備知識真的很少。

零、安裝

React 的安裝包,可以到官網下載。不過,React Demos 已經自帶 React 源碼,不用另外安裝,只需把這個庫拷貝到你的硬碟就行了。

$ git clone git@github.com:ruanyf/react-demos.git

如果你沒安裝 git, 那就直接下載 zip 壓縮包。

下面要講解的12個例子在各個 Demo 子目錄,每個目錄都有一個 index.html 文件,在瀏覽器打開這個文件(大多數情況下雙擊即可),就能立刻看到效果。

需要說明的是,React 可以在瀏覽器運行,也可以在伺服器運行,但是本教程只涉及瀏覽器。一方面是為了盡量保持簡單,另一方面 React 的語法是一致的,伺服器的用法與瀏覽器差別不大。Demo13 是伺服器首屏渲染的例子,有興趣的朋友可以自己去看源碼。

一、HTML 模板

使用 React 的網頁源碼,結構大致如下。

& & & &