一憔恳、優(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采用hashCode和valueOf對(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ì)象hashCode和valueOf,只要兩個(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 屬性值變化后,
【注】
整棵樹就是一個(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)指正钥平,感謝。