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