【React】—組件性能優(yōu)化

一、優(yōu)化原理

改寫react生命周期shouldComponentUpdate,使其在需要重新渲染當(dāng)前組件時返回true,否則返回false。不再全部返回true。

二、主流優(yōu)化方式

1.react官方解決方案

原理:重寫默認(rèn)的shouldComponentUpdate,將舊props、state與新props、state逐個進(jìn)行淺比較(形如:this.props.option === nextProps.option ?? false : true),如果全部相同,返回false,如果有不同,返回true。

PureRenderMixin(es5):

var PureRenderMixin = require('react-addons-pure-render-mixin');

????React.createClass({

????mixins: [PureRenderMixin],

? ? render: function() {

? ? ? ? ?return <div className={this.props.className}>foo</div>;


? ? }

});

Shallow Compare (es6):

var shallowCompare = require('react-addons-shallow-compare');

export class SampleComponent extends React.Component {

????shouldComponentUpdate(nextProps, nextState) {

????????return shallowCompare(this, nextProps, nextState);

????}

????render() {

????????return <div className={this.props.className}>foo</div>
????}

}

es7裝飾器的寫法:

import pureRender from "pure-render-decorator"

...

@pureRender

class SampleComponent extends Component {

????render() {

????????return (

? ? ????????<div className={this.props.className}>foo</div>

????????)

????}

}

react 15.3.0+寫法(用來替換react-addons-pure-render-mixin):

class?SampleComponent extends React.PureComponent{

? ? render(){

? ? ? ? return(

? ? ? ? ? ? <div className={this.props.className}>foo</div>

????????)

????}

}

*上述方案存在問題(淺比較的問題):

(1)某些props、state值未改變的情況,返回true,例如:

?<Cell options={this.props.options || [ ]} />

當(dāng)this.props.options == false時,options=[ ]。當(dāng)父組件兩次渲染,this.props.options一直 == false,對于Cell組件來說,options沒有改變,不需要重新渲染。但Cell的shouldComponentUpdate中進(jìn)行的是淺比較,由于[ ] !== [ ],所以,this.props.options === nextProps.options為false,shouldComponentUpdate會返回true,Cell將進(jìn)行重新渲染。

解決方法如下:

const default = [ ];

<Cell options={this.props.options || default} />

(2)某些props、state值改變的情況,返回false,例如:

handleClick() {

????let {items} = this.state;

????items.push('new-item') ;

????this.setState({ items });

}

render() {

????return (

????????<div>

????????????<button onClick={this.handleClick} />

????????????<ItemList items={this.state.items} />

????????</div>
????)

}

如果ItemList是純組件(PureComponent),那么這時它是不會被渲染的。因為盡管this.state.items的值發(fā)生了改變,但是它仍然指向同一個對象的引用。




2.Immutable

原理:Persistent Data Structure(持久化數(shù)據(jù)結(jié)構(gòu)),也就是使用舊數(shù)據(jù)創(chuàng)建新數(shù)據(jù)時,要保證舊數(shù)據(jù)同時可用且不變。

Immutable Data就是一旦被創(chuàng)建,就不能再更改的數(shù)據(jù)。對 Immutable 對象的任何修改或添加刪除操作都會返回一個新的 Immutable 對象。同時,為了避免 deepCopy 把所有節(jié)點都復(fù)制一遍帶來的性能損耗,Immutable 使用了?Structural Sharing(結(jié)構(gòu)共享),即如果對象樹中一個節(jié)點發(fā)生變化,只修改這個節(jié)點和受它影響的父節(jié)點,其它節(jié)點則進(jìn)行共享。

【在react中使用immutable】

改變shouldComponentUpdate的重新渲染規(guī)則

(1)防止每次setState或傳遞props,即使state和props的值沒有發(fā)生改變也重新渲染組件,帶來無謂的性能消耗

(2)防止淺比較帶來的比較誤差問題,以及深比較帶來的性能消耗問題。

import { is, fromJS } from 'immutable';

constructor(){

? ??this.state = {

????????person: fromJS({

????????????????name: 'xuxuan',

????????????????age: 12?

????????});

? ?}

}

componentDidMount(){

????this.setState({

????????person: this.state.person.update('name', v => v + 'update')

????});

}

shouldComponentUpdate: (nextProps, nextState) => {

????return !(this.props === nextProps || is(this.props, nextProps))

????????????????||

? ? ? ? ????? !(this.state === nextState || is(this.state, nextState));

}

!!注意】state 本身必須是普通對象,但是里面的值可以是 immutable 的數(shù)據(jù)。

this.state和nextState是兩個普通對象,通過is方法判斷二者的值是否相同。


從setState到re-render整個過程(個人理解)

(1)setState中設(shè)置person的name值,由于person是immutable的,因此,這里會開辟一塊新的內(nèi)存,存儲設(shè)置后的name(即使name的值沒有真的發(fā)生改變,只要對immutable數(shù)據(jù)進(jìn)行了設(shè)置操作,就會生成一個全新的對象),同時,開辟新內(nèi)存,存儲受name影響的immutable的各級父節(jié)點,這里只有person。而,其余未受影響的節(jié)點,將進(jìn)行內(nèi)存共享,如這里的age。由此,形成了新的state對象,即:nextState。

(2)setState(nextState)操作觸發(fā)shouldComponentUpdate生命周期執(zhí)行。

(3)shouldComponentUpdate中使用immutable.js的is方法對比兩個對象。

????????這里,is采用hashCodevalueOf對比兩個對象是否相同:

? ? ? ? *當(dāng)對象的值沒有改變,is返回true,組件不重新渲染。依然存儲舊的state(nextState大概會被銷毀吧)

? ? ? ? *當(dāng)對象的值發(fā)生改變,is返回false,組件重新渲染。nextState替換state(state就不能再訪問了,但它的immutable的屬性應(yīng)該還可以)

(4)當(dāng)需要重新渲染的時候,對于state來說,就是用新的nextState替換舊的state。由于state是普通對象,因此可以被更改,被替換。


【關(guān)于is方法】

Immutable的is比較的是這個對象hashCodevalueOf,只要兩個對象的hashCode相等,值就是相同的,避免了深度遍歷,提高了性能。

擴(kuò)展:

hashCode的比較是將兩個對象String化(eg:{a:111} —> '{a:111}')后,比較兩個字符串對應(yīng)位置字符的charCode是否相同,完全相同則認(rèn)為是兩個相同的對象。


使用?Immutable?后,如下圖,當(dāng)紅色節(jié)點的?state 屬性值變化后,


Immutable數(shù)據(jù)樹結(jié)構(gòu)變化示意圖

【注】

整棵樹就是一個Immutable的對象。

當(dāng)紅色節(jié)點發(fā)生改變,為這個節(jié)點及其父節(jié)點開辟新的內(nèi)存,存儲新數(shù)據(jù),其他藍(lán)色節(jié)點不變,共享之前的內(nèi)存。


immutable.js框架是非常好的Immutable庫,其他可用api,詳見官方文檔。

使用原則:

由于侵入性較強,新項目引入比較容易,老項目遷移需要謹(jǐn)慎評估遷移成本。對于一些提供給外部使用的公共組件,最好不要把Immutable對象直接暴露在對外的接口中。


【!!Mark】

個人理解部分,如有理解偏差,請指正,感謝。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容