React
創建並保存一渲染UI的鏡像,通常被稱爲virtual DOM
,當組件的props
或者state
發生變化時,React
會通過對比虛擬DOM和新返回的元素,如果二者不同,則更新DOM。
在某些情況下,可以通過shouldComponentUpdate
生命週期函數來加速這種對比的過程。此函數在重新渲染前觸發,默認返回true
,React
執行更新:
shouldComponentUpdate(nextProps, nextState) {
return true;
}
如果你知道在哪種情況下,組件不需要更新,可以在此函數中返回false
來跳過整個渲染的過程,包括調用本組件和以後組件中的render()
SCU代表shouldComponentUpdate
返回值,綠色true
紅色false
C2節點shouldComponentUpdate
返回false
,React
不需要更新C2,甚至不用再去和C4 C5對比了。
C1 C3 SCU返回true
,繼續向下對比C6, SCU返回true
,vDOMEq檢測不相同,更新C6
雖然C3 SCU返回true
,繼續向下對比的時候C7跟以前渲染的DOM節點相同,所以不再更新
C8 對比DOM時相同,就不再更新了;而C2 C7時shouldComponentUpdate
返回了false
,所以甚至不用對比DOM,就可以直接跳過了,也不用調用render
示例 如果只有在props.color
和state.count
變化時才更新組件
class CounterButton extends React.Component {
constructor(props) {
super(props);
this.state = {count: 1};
}
shouldComponentUpdate(nextProps, nextState) {
if (this.props.color !== nextProps.color) {
return true;
}
if (this.state.count !== nextState.count) {
return true;
}
return false;
}
render() {
return (
<button
color={this.props.color}
onClick={() => this.setState(state => ({count: state.count + 1}))}>
Count: {this.state.count}
</button>
);
}
}
如果業務變的複雜了,這種逐一判斷的方式就不太實用了,React
提供了一種隱式對比的helper
,繼承自React.PureComponent
,以上代碼就可以簡化爲:
class CounterButton extends React.PureComponent {
constructor(props) {
super(props);
this.state = {count: 1};
}
render() {
return (
<button
color={this.props.color}
onClick={() => this.setState(state => ({count: state.count + 1}))}>
Count: {this.state.count}
</button>
);
}
}
值得注意的是,props
和state
如果以一種隱式判斷不識別的方式寫的時候,這種方法就不起作用了,比如有這樣一個需求,父組件WordAdder
調用子組件ListOfWords
,每點擊一次按鈕,增加一個單詞marklar
,按通常思維有可能會按以下代碼寫
class ListOfWords extends React.PureComponent {
render() {
return <div>{this.props.words.join(',')}</div>;
}
}
class WordAdder extends React.Component {
constructor(props) {
super(props);
this.state = {
words: ['marklar']
};
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
// This section is bad style and causes a bug
const words = this.state.words;
words.push('marklar');
this.setState({words: words});
}
render() {
return (
<div>
<button onClick={this.handleClick} />
<ListOfWords words={this.state.words} />
</div>
);
}
}
問題是PureComponent
只會把this.props.words
的新舊值做一個簡單的對比。儘管數組words
在handleClick
方法中發生了改變,但是覆蓋了上一個生命週期中的state
,所以在新舊對比的時候state
是相同的,不會觸發重新渲染。 因此就有了下面要說的不修改數據
最簡單的方法就是不修改數據,上面示例中的handleClick
可以重寫爲
handleClick() {
this.setState(prevState => ({
words: prevState.words.concat(['marklar'])
}));
}
//或者
handleClick() {
this.setState(prevState => ({
words: [...prevState.words, 'marklar'],
}));
};