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

一、優(yōu)化原理

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

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

1.react官方解決方案

原理:重寫默認(rèn)的shouldComponentUpdate,將舊props、state與新props、state逐個(gè)進(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時(shí),options=[ ]。當(dāng)父組件兩次渲染,this.props.options一直 == false,對(duì)于Cell組件來說,options沒有改變,不需要重新渲染。但Cell的shouldComponentUpdate中進(jìn)行的是淺比較,由于[ ] !== [ ],所以,this.props.options === nextProps.options為false,shouldComponentUpdate會(huì)返回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),那么這時(shí)它是不會(huì)被渲染的。因?yàn)楸M管this.state.items的值發(fā)生了改變,但是它仍然指向同一個(gè)對(duì)象的引用。




2.Immutable

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

Immutable Data就是一旦被創(chuàng)建,就不能再更改的數(shù)據(jù)。對(duì) Immutable 對(duì)象的任何修改或添加刪除操作都會(huì)返回一個(gè)新的 Immutable 對(duì)象。同時(shí),為了避免 deepCopy 把所有節(jié)點(diǎn)都復(fù)制一遍帶來的性能損耗,Immutable 使用了?Structural Sharing(結(jié)構(gòu)共享),即如果對(duì)象樹中一個(gè)節(jié)點(diǎn)發(fā)生變化,只修改這個(gè)節(jié)點(diǎn)和受它影響的父節(jié)點(diǎn),其它節(jié)點(diǎn)則進(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 本身必須是普通對(duì)象,但是里面的值可以是 immutable 的數(shù)據(jù)。

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


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

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

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

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

????????這里,is采用hashCodevalueOf對(duì)比兩個(gè)對(duì)象是否相同:

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

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

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


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

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

擴(kuò)展:

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


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


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

【注】

整棵樹就是一個(gè)Immutable的對(duì)象。

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


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

使用原則:

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


【!!Mark】

個(gè)人理解部分,如有理解偏差,請(qǐng)指正,感謝。

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

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

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