一、優(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采用hashCode和valueOf對比兩個對象是否相同:
? ? ? ? *當(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比較的是這個對象hashCode和valueOf,只要兩個對象的hashCode相等,值就是相同的,避免了深度遍歷,提高了性能。
擴(kuò)展:
hashCode的比較是將兩個對象String化(eg:{a:111} —> '{a:111}')后,比較兩個字符串對應(yīng)位置字符的charCode是否相同,完全相同則認(rèn)為是兩個相同的對象。
使用?Immutable?后,如下圖,當(dāng)紅色節(jié)點的?state 屬性值變化后,

【注】
整棵樹就是一個Immutable的對象。
當(dāng)紅色節(jié)點發(fā)生改變,為這個節(jié)點及其父節(jié)點開辟新的內(nèi)存,存儲新數(shù)據(jù),其他藍(lán)色節(jié)點不變,共享之前的內(nèi)存。
immutable.js框架是非常好的Immutable庫,其他可用api,詳見官方文檔。
使用原則:
由于侵入性較強,新項目引入比較容易,老項目遷移需要謹(jǐn)慎評估遷移成本。對于一些提供給外部使用的公共組件,最好不要把Immutable對象直接暴露在對外的接口中。
【!!Mark】
個人理解部分,如有理解偏差,請指正,感謝。