React性能优化 PureComponent
為什么使用?
React15.3中新加了一個(gè)?PureComponent?類,顧名思義,?pure?是純的意思,?PureComponent?也就是純組件,取代其前身?PureRenderMixin?,?PureComponent?是優(yōu)化?React?應(yīng)用程序最重要的方法之一,易于實(shí)施,只要把繼承類從?Component?換成?PureComponent?即可,可以減少不必要的?render操作的次數(shù),從而提高性能,而且可以少寫?shouldComponentUpdate?函數(shù),節(jié)省了點(diǎn)代碼。
原理
當(dāng)組件更新時(shí),如果組件的?props?和?state?都沒發(fā)生改變,?render?方法就不會(huì)觸發(fā),省去?Virtual DOM?的生成和比對(duì)過程,達(dá)到提升性能的目的。具體就是?React?自動(dòng)幫我們做了一層淺比較:
| 1 2 3 4 | if?(this._compositeType === CompositeTypes.PureClass) { shouldUpdate = !shallowEqual(prevProps, nextProps) || !shallowEqual(inst.state, nextState); } |
?
而?shallowEqual?又做了什么呢?會(huì)比較?Object.keys(state | props)?的長(zhǎng)度是否一致,每一個(gè)?key?是否兩者都有,并且是否是一個(gè)引用,也就是只比較了第一層的值,確實(shí)很淺,所以深層的嵌套數(shù)據(jù)是對(duì)比不出來(lái)的。
使用指南
易變數(shù)據(jù)不能使用一個(gè)引用
案例:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | class?App?extends?PureComponent?{ state = { items: [1,?2,?3] } handleClick =?()?=>?{ const?{ items } =?this.state; items.pop(); this.setState({ items }); } render() { return?(<div> <ul> {this.state.items.map(i =>?<li?key={i}>{i}</li>)} </ul> <button?onClick={this.handleClick}>delete</button> </div>) } } |
?
會(huì)發(fā)現(xiàn),無(wú)論怎么點(diǎn)?delete?按鈕,?li?都不會(huì)變少,因?yàn)?items?用的是一個(gè)引用,?shallowEqual?的結(jié)果為?true?。改正:
| 1 2 3 4 5 | handleClick =?()?=>?{ const?{ items } =?this.state; items.pop(); this.setState({?items: [].concat(items) }); } |
?
這樣每次改變都會(huì)產(chǎn)生一個(gè)新的數(shù)組,也就可以?render?了。這里有一個(gè)矛盾的地方,如果沒有?items.pop();?操作,每次?items?數(shù)據(jù)并沒有變,但還是?render?了,這不就很操蛋么?呵呵,數(shù)據(jù)都不變,你?setState?干嘛?
不變數(shù)據(jù)使用一個(gè)引用
子組件數(shù)據(jù)
上面易變數(shù)據(jù)不能使用一個(gè)引用的案例中有一個(gè)點(diǎn)擊刪除操作,如果我們刪除的代碼這么寫:
| 1 2 3 4 5 | handleClick =?()?=>?{ const?{ items } =?this.state; items.splice(items.length -?1,?1); this.setState({ items }); } |
?
items?的引用也是改變的,但如果?items?里面是引用類型數(shù)據(jù):
| 1 | items: [{a:?1}, {a:?2}, {a:?3}] |
?
這個(gè)時(shí)候
| 1 | state.items[0] === nextState.items[0]?// false |
?
子組件里還是re-render了。這樣就需要我們保證不變的子組件數(shù)據(jù)的引用不能改變。這個(gè)時(shí)候可以使用immutable-js函數(shù)庫(kù)。
函數(shù)屬性
我們?cè)诮o組件傳一個(gè)函數(shù)的時(shí)候,有時(shí)候總喜歡:
| 1 2 3 4 5 6 7 8 9 | // 1 <MyInput onChange={e =>?this.props.update(e.target.value)} /> // 2 update(e) { this.props.update(e.target.value) } render() { return?<MyInput?onChange={this.update.bind(this)}?/> } |
?
由于每次?render?操作?MyInput?組件的?onChange?屬性都會(huì)返回一個(gè)新的函數(shù),由于引用不一樣,所以父組件的?render?也會(huì)導(dǎo)致?MyInput?組件的?render?,即使沒有任何改動(dòng),所以需要盡量避免這樣的寫法,最好這樣寫:
| 1 2 3 4 5 6 7 | // 1,2 update =?(e) =>?{ this.props.update(e.target.value) } render() { return?<MyInput?onChange={this.update}?/> } |
?
空對(duì)象、空數(shù)組或固定對(duì)象
有時(shí)候后臺(tái)返回的數(shù)據(jù)中,數(shù)組長(zhǎng)度為0或者對(duì)象沒有屬性會(huì)直接給一個(gè)?null?,這時(shí)候我們需要做一些容錯(cuò):
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | class?App?extends?PureComponent?{ state = { items: [{?name:?'test1'?},?null, {?name:?'test3'?}] } store =?(id, value) =>?{ const?{ items } =?this.state; items[id] = assign({}, items[id], {?name: value }); this.setState({?items: [].concat(items) }); } render() { return?(<div> <ul> {this.state.items.map((i, k) => <Item?style={{?color:?'red' }}?store={this.store}?key={k}?id={k}?data={i?|| {}} />) } </ul> </div>) } } |
?
當(dāng)某一個(gè)子組件調(diào)用?store?函數(shù)改變了自己的那條屬性,觸發(fā)?render?操作,如果數(shù)據(jù)是?null?的話?data?屬性每次都是一個(gè)?{},{} ==== {}?是?false?的,這樣無(wú)端的讓這幾個(gè)子組件重新?render?了。{ color: 'red' }也是一樣。
最好設(shè)置一個(gè)?defaultValue?為?{},如下:
| 1 2 3 | static?defaultValue = {} const?style = {?color:?'red'?}; <Item?style={style}?store={this.store}?key={k}?id={k}?data={i?||?defaultValue} /> |
?
復(fù)雜狀態(tài)與簡(jiǎn)單狀態(tài)不要共用一個(gè)組件
這點(diǎn)可能和?PureComponent?沒多少關(guān)系,但做的不好可能會(huì)浪費(fèi)很多性能,比如一個(gè)頁(yè)面上面一部分是一個(gè)復(fù)雜的列表,下面是一個(gè)輸入框,抽象代碼:
| 1 2 3 4 5 6 7 8 9 10 11 | change =?(e) =>?{ this.setState({?value: e.target.value }); } render() { return?(<div> <ul> {this.state.items.map((i, k) =>?<li?key={k}>?{...}</li>)} </ul> <input?value={this.state.value}?onChange={this.change}?/> </div>) } |
?
表單和列表其實(shí)是沒有什么關(guān)聯(lián)的,表單的值也可能經(jīng)常變動(dòng),但它的會(huì)給列表也帶來(lái)必然的?diff操作,這是沒必要的,最好是給列表抽出成一個(gè)單獨(dú)的?PureComponent?組件,這樣?state.items?不變的話,列表就不會(huì)重新?render?了。
與?shouldComponentUpdate?共存
如果?PureComponent?里有?shouldComponentUpdate?函數(shù)的話,直接使用?shouldComponentUpdate的結(jié)果作為是否更新的依據(jù),沒有?shouldComponentUpdate?函數(shù)的話,才會(huì)去判斷是不是?PureComponent?,是的話再去做?shallowEqual?淺比較。
| 1 2 3 4 5 6 7 8 9 10 11 | // 這個(gè)變量用來(lái)控制組件是否需要更新 var?shouldUpdate =?true; // inst 是組件實(shí)例 if?(inst.shouldComponentUpdate) { shouldUpdate = inst.shouldComponentUpdate(nextProps, nextState, nextContext); }?else?{ if?(this._compositeType === CompositeType.PureClass) { shouldUpdate = !shallowEqual(prevProps, nextProps) || !shallowEqual(inst.state, nextState); } } |
?
老版本兼容寫法
| 1 2 3 4 5 | import?React { PureComponent, Component }?from?'react'; class?Foo?extends?(PureComponent?||?Component)?{ //... } |
這樣在老版本的?React?里也不會(huì)掛掉。
總結(jié)
PureComponent?真正起作用的,只是在一些純展示組件上,復(fù)雜組件用了也沒關(guān)系,反正?shallowEqual?那一關(guān)就過不了,不過記得?props?和?state?不能使用同一個(gè)引用哦。
轉(zhuǎn)載于:https://www.cnblogs.com/luckyXcc/p/9143734.html
總結(jié)
以上是生活随笔為你收集整理的React性能优化 PureComponent的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: [UE4]给Widget增加参数,Pre
- 下一篇: for-each 循环原理