英文原文:React.js Best Practices for 2016
作者:Péter Márton
轉(zhuǎn)載自AlloyTeam:
http://www.alloyteam.com/2016/01/reactjs-best-practices-for-2016/
譯者按:近幾個月React相關(guān)話題依舊火熱,相信越來越多的開發(fā)者在嘗試這樣一項(xiàng)技術(shù)盏袄,我們團(tuán)隊(duì)也在PC和移動端不斷總結(jié)經(jīng)驗(yàn)芬探。2016來了,這應(yīng)該是React走向成熟的一年璃搜,不管你是新手褐澎,還是已經(jīng)對React有所了解亲善,是時候總結(jié)一下最佳實(shí)踐了凉袱,讓我們看看國外的開發(fā)者總結(jié)了哪些好的實(shí)踐吧~
2015可以算是React之年了傲宜,關(guān)于其版本發(fā)布和開發(fā)者大會的話題遍布全球运杭。關(guān)于去年React的發(fā)展里程碑詳情,可以查看我們整理的React 2015這一年函卒。
2016年最有趣的問題可能是辆憔,我們該如何編寫一個應(yīng)用呢,有什么推薦的庫或框架?
作為一個長時間使用React.js的開發(fā)者躁愿,我已經(jīng)有自己的答案和最佳實(shí)踐了叛本,但你可能不會同意我說的所有點(diǎn)。我對你的想法和意見很感興趣彤钟,請留言進(jìn)行討論来候。
如果你只是剛開始接觸React.js,請閱讀React.js教程逸雹,或Pete Hunt的React howto营搅。
數(shù)據(jù)處理
在React.js應(yīng)用中處理數(shù)據(jù)超級簡單的,但同時還是有些挑戰(zhàn)梆砸。
這是因?yàn)槟憧梢允褂枚喾N方式转质,來給一個React組件傳遞屬性數(shù)據(jù),從而構(gòu)建出渲染樹帖世。但這種方式并不總是能明顯地看出休蟹,你是否應(yīng)該更新某些視圖。
2015開始涌現(xiàn)出一批具有更強(qiáng)功能和響應(yīng)式解決方案的Flux庫日矫,讓我們一起看看:
Flux
根據(jù)我們的經(jīng)驗(yàn)赂弓,F(xiàn)lux通常被過度使用了(就是大家在不需使用的場景下,還是使用了)哪轿。
Flux提供了一種清爽的方式存儲和管理應(yīng)用的狀態(tài)盈魁,并在需要的時候觸發(fā)渲染。
Flux對于那些應(yīng)用的全局state(譯者注:為了對應(yīng)React中的state概念窃诉,本文將不對state進(jìn)行翻譯)特別有用杨耙,比如:管理登錄用戶的狀態(tài)、路由狀態(tài)飘痛,或是活躍賬號狀態(tài)珊膜。如果使用臨時變量或者本地?cái)?shù)據(jù)來處理這些狀態(tài),會非常讓人頭疼敦冬。
我們不建議使用Flux來管理路由相關(guān)的數(shù)據(jù)辅搬,比如/items/:itemId。應(yīng)該只是獲取它并存在組件的state中脖旱,這種情況下堪遂,它會在組件銷毀時一起被銷毀。
如果需要Flux的更多信息萌庆,建議閱讀The Evolution of Flux Frameworks溶褪。
使用Redux
Redux是一個JavaScript app的可預(yù)測state容器。
如果你覺得需要Flux或者相似的解決方案践险,你應(yīng)該了解一下redux猿妈,并學(xué)習(xí)Dan Abramov的redux入門指南培愁,來強(qiáng)化你的開發(fā)技能们衙。
Rudux發(fā)展了Flux的思想惧互,同時降低了其復(fù)雜度鱼的。
扁平化state
API通常會返回嵌套的資源,這讓Flux或Redux架構(gòu)很難處理俯抖。我們推薦使用normalizr這類庫來盡可能地扁平化state输瓜。
像這樣:
const data = normalize(response, arrayOf(schema.user))
state = _.merge(state, data.entities)
(我們使用isomorphic-fetch與API進(jìn)行通信)
使用immutable state
共享的可變數(shù)據(jù)是罪惡的根源——Pete Hunt, React.js Conf 2015
不可變對象是指在創(chuàng)建后不可再被修改的對象。
不可變對象可以減少那些讓我們頭痛的工作芬萍,并且通過引用級的比對檢查來提升渲染性能尤揣。比如在shouldComponentUpdate中:
shouldComponentUpdate(nexProps) {
// 不進(jìn)行對象的深度對比
return this.props.immutableFoo !== nexProps.immutableFoo
}
如何在JavaScript中實(shí)現(xiàn)不可變
比較麻煩的方式是,小心地編寫下面的例子柬祠,總是需要使用deep-freeze-node(在變動前進(jìn)行凍結(jié)北戏,結(jié)束后驗(yàn)證結(jié)果)進(jìn)行單元測試。
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非持ビ玻快蚜点,其背后的思想也非常美妙轧房。就算沒準(zhǔn)備使用它,還是推薦你去看看Lee Byron的視頻Immutable Data and React绍绘,可以了解到它內(nèi)部的實(shí)現(xiàn)原理奶镶。
Observables and reactive解決方案
如果你不喜歡Flux/Redux,或者想要更加reactive陪拘,不用失望厂镇!還有很多方案供你選擇,這里是你可能需要的:
- cycle.js(“一個更清爽的reactive框架”)
- rx-flux(“Flux與Rxjs結(jié)合的產(chǎn)物”)
- redux-rx(“Redux的Rxjs工具庫”)
- mobservable(“可觀測的數(shù)據(jù)左刽,reactive的功能捺信,簡潔的代碼”)
路由
現(xiàn)在幾乎所有app都有路由功能。如果你在瀏覽器中使用React.js欠痴,你將會接觸到這個點(diǎn)迄靠,并為其選擇一個庫。
我們選擇的是出自優(yōu)秀rackt社區(qū)的react-router喇辽,這個社區(qū)總是能為React.js愛好者們帶來高質(zhì)量的資源掌挚。
要使用react-router需要查看它的文檔,但更重要的是:如果你使用Flux/Redux菩咨,我們推薦你將路由state與store或全局state保持同步吠式。
同步路由state可以讓Flux/Redux來控制路由行為陡厘,并讓組件讀取到路由信息。
Redux的用戶可以使用redux-simple-router來省點(diǎn)事兒特占。
代碼分割糙置,懶加載
只有一小部分webpack的用戶知道,應(yīng)用代碼是可以分割成多個js包的是目。
require.ensure([], () => {
const Profile = require('./Profile.js')
this.setState({
currentComponent: Profile
})
})
這對于大型應(yīng)用十分有用罢低,因?yàn)橛脩魹g覽器不用下載那些很少會使用到的代碼,比如Profile頁胖笛。
多js包會導(dǎo)致額外的HTTP請求數(shù)网持,但對于HTTP/2的多路復(fù)用,完全不是問題长踊。
與chunk hashing 結(jié)合可以優(yōu)化緩存命中率功舀。
下個版本的react-router將會對代碼分隔做更多支持。
對于react-router的未來規(guī)劃身弊,可以去看博文Ryan Florence: Welcome to Future of Web Application Delivery辟汰。
組件
很多人都在抱怨JSX,但首先要知道阱佛,它只是React中可選的一項(xiàng)能力帖汞。
最后,它們都會被Bable編譯成JavaScript凑术。你可以繼續(xù)使用JavaScript編寫代碼翩蘸,但是在處理HTML時使用JSX會感覺更自然。特別是對于那些不懂js的人淮逊,他們可以只修改HTML相關(guān)的部分催首。
JSX是一個類似于XML的JavaScript擴(kuò)展,可以配合一個簡單的語法編譯工具來使用它泄鹏。——深入淺出JSX
如果你想了解更多JSX的內(nèi)容备籽,查看文章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更像是一個語法問題霉猛,而不是技術(shù)問題。(譯者注:在Class語法中诈唬,React組件的mixins方法將無法使用韩脏。)我們認(rèn)為使用createClass和React.Component沒有對錯之分。
屬性類型(PropType)
如果你以前不檢查props的類型铸磅,那么2016你應(yīng)該開始改正了赡矢。它會幫你節(jié)省未來很多時間杭朱,相信我。
MyComponent.propTypes = {
isLoading: PropTypes.bool.isRequired,
items: ImmutablePropTypes.listOf(
ImmutablePropTypes.contains({
name: PropTypes.string.isRequired,
})
).isRequired
}
是的吹散,同時也盡可能使用react-immutable-proptypes檢查Immutable.js的props弧械。
高階組件(Higher order components)
minins將死,ES6的Class將不對其進(jìn)行支持空民,我們需要尋找新的方法刃唐。
什么是高階組件?
PassData({ foo: 'bar' })(MyComponent)
簡單地界轩,你創(chuàng)建一個從原生組件繼承下來的組件画饥,并且擴(kuò)展了原始組件的行為。你可以在多種場景來使用它浊猾,比如鑒權(quán):requireAuth({ role: 'admin' })(MyComponent)(在高階組件中檢查用戶權(quán)限抖甘,如果還沒有登錄就進(jìn)行跳轉(zhuǎn)),或者將組件與Flux/Redux的store相連通葫慎。
在RisingStack衔彻,我們也喜歡分離數(shù)據(jù)拉取和controller類的邏輯到高階組件中,這樣可以盡可能地保持view層的簡單偷办。
測試
好的代碼覆蓋測試是開發(fā)周期中的重要一環(huán)艰额。幸運(yùn)的是,React.js社區(qū)有很多這樣的庫來幫助我們椒涯。
組件測試
我們最喜愛的組件測試庫是AirBnb的enzyme柄沮。有了它的淺渲染特性,可以對組件的邏輯和渲染結(jié)果進(jìn)行測試逐工,非常棒對不對铡溪?它現(xiàn)在還不能替代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非常簡單纬纪,它響應(yīng)actions然后將原來的state轉(zhuǎn)為新的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)
})
關(guān)于更深入的redux測試,請參考官方文檔问畅。
使用npm
雖然React.js并不依賴代碼構(gòu)建工具娃属,我們推薦Webpack和Browserify六荒,它們都具有npm出色的能力。Npm有很多React.js的package矾端,還可以幫助你優(yōu)雅地管理依賴掏击。
(請不要忘記復(fù)用你自己的組件,這是優(yōu)化代碼的絕佳方式秩铆。)
包大醒馔ぁ(Bundle size)
這本身不是一個React相關(guān)的問題,但多數(shù)人都會對其React進(jìn)行打包殴玛,所以我在這里提一下捅膘。
當(dāng)你對源代碼進(jìn)行構(gòu)建時,要保持對包大小的關(guān)注滚粟。要將其控制在最小體積篓跛,你需要思考如何require/import依賴。
查看下面的代碼片段坦刀,有兩種方式可以對輸出產(chǎn)生重大影響:
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鲤遥,因?yàn)榈谌酱a的更新頻率比我們自己帶嗎低很多沐寺。
對輸出文件進(jìn)行hash命名(WebPack中的chunk hash),并使用長緩存盖奈,我們可以顯著地減少訪問用戶需要下載的代碼混坞。結(jié)合代碼懶加載,優(yōu)化效果可想而知钢坦。
如果你對WebPack還很陌生究孕,可以去看超贊的React webpack指南。
組件級的hot reload
如果你曾使用livereload寫過單頁面應(yīng)用爹凹,你可能知道當(dāng)在處理一些與狀態(tài)相關(guān)的事情厨诸,一點(diǎn)代碼保存整個頁面就刷新了,這種體驗(yàn)有多煩人禾酱。你需要逐步點(diǎn)擊操作到剛才的環(huán)節(jié)微酬,然后在這樣的重復(fù)中奔潰。
在React開發(fā)中颤陶,是可以reload一個組件颗管,同時保持它的state不變——耶,從此無需苦惱滓走!
搭建hot reload垦江,可參考react-transform-boilerplate。
使用ES2015
前面提到過搅方,在React.js中使用的JSX比吭,最終會被Babel.js進(jìn)行編譯绽族。
Bable的能力還不止這些,它可以讓我們在瀏覽器中放心地使用ES6/ES2015梗逮。在RisingStack项秉,我們在服務(wù)器端和客戶端都使用了ES2015的特性,ES2015已經(jīng)可以在最新的LTS Node.js版本中使用了慷彤。
代碼檢查(Linters)
也許你已經(jīng)對你的代碼制定了代碼規(guī)范娄蔼,但是你知道React的各種代碼規(guī)范嗎?我們建議你選擇一個代碼規(guī)范底哗,然后照著下面說的來做岁诉。
在RisingStack,我們強(qiáng)制將linters運(yùn)行在持續(xù)集成(CI)系統(tǒng)跋选,已經(jīng)git push功能上涕癣。查看pre-push和pre-commit。
我們使用標(biāo)準(zhǔn)的JavaScript代碼風(fēng)格前标,并使用eslint-plugin-react來檢查React.js代碼坠韩。
(是的,我們已經(jīng)不再使用分號了)
GraphQL和Relay
GraphQL和Relay是相關(guān)的新技術(shù)炼列。在RisingStack只搁,我們不在生產(chǎn)環(huán)境使用它們,暫時保持關(guān)注俭尖。
我們寫了一個Relay的MongoDB ORM氢惋,叫做graffiti,可以使用你已有的mongoose models來創(chuàng)建GraphQL server稽犁。
如果你想學(xué)習(xí)這些新技術(shù)焰望,我們建議你去看看這個庫,然后寫幾個demo玩玩已亥。
這些React.js最佳實(shí)踐的核心點(diǎn)
有些優(yōu)秀的技術(shù)和庫其實(shí)跟React都沒什么關(guān)系熊赖,關(guān)鍵在于要關(guān)注社區(qū)都在做些什么。2015這一年陷猫,React社區(qū)被Elm架構(gòu)啟發(fā)了很多秫舌。
如果你知道其他2016年大家應(yīng)該使用的React.js工具,請留言告訴我們绣檬。