[React] render中进行diff
场景
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
class A extends Component {
componentWillMount() {
console.log('A: componentWillMount');
}
render() {
console.log('A: render');
return (
<div>
<div>456</div>
<div>A{this.props.a}</div>
</div>
);
}
componentDidMount() {
console.log('A: componentDidMount');
}
componentWillReceiveProps() {
console.log('A: componentWillReceiveProps');
}
shouldComponentUpdate() {
console.log('A: shouldComponentUpdate');
return true;
}
componentWillUpdate() {
console.log('A: componentWillUpdate');
}
// render
componentDidUpdate() {
console.log('A: componentDidUpdate');
}
}
class Page extends Component {
state = {
a: 1
};
componentWillMount() {
console.log('Page: componentWillMount');
}
render() {
console.log('Page: render');
return (
<div>
<div>123</div>
<A a={this.state.a} />
</div>
);
}
componentDidMount() {
console.log('Page: componentDidMount');
setTimeout(() => {
console.warn('Page: setState');
this.setState({
a: 1
});
}, 2000);
}
componentWillReceiveProps() {
console.log('Page: componentWillReceiveProps');
}
shouldComponentUpdate() {
console.log('Page: shouldComponentUpdate');
return true;
}
componentWillUpdate() {
console.log('Page: componentWillUpdate');
}
// render
componentDidUpdate() {
console.log('Page: componentDidUpdate');
}
}
ReactDOM.render(
<Page />,
document.getElementById('app')
);
解答
1. 日志分析
(1)即使没有改变state
,也会调用shouldComponentUpdate
。
this.setState({a:2});
(改变了state
)和this.setState({a:1});
(没有改变state
)日志结果一样。
// 当前组件和子组件shouldComponentUpdate都为true
Page: componentWillMount
Page: render
A: componentWillMount
A: render
A: componentDidMount
Page: componentDidMount
Page: setState
Page: shouldComponentUpdate ---- true
Page: componentWillUpdate
Page: render
A: componentWillReceiveProps
A: shouldComponentUpdate ---- true
A: componentWillUpdate
A: render
A: componentDidUpdate
Page: componentDidUpdate
(2)如果A
组件的shouldComponentUpdate
返回false
。
那么A
组件的componentWillUpdate
render
componentDidUpdate
就都不执行了。
// 子组件shouldComponentUpdate为false
Page: componentWillMount
Page: render
A: componentWillMount
A: render
A: componentDidMount
Page: componentDidMount
Page: setState
Page: shouldComponentUpdate ---- true
Page: componentWillUpdate
Page: render
A: componentWillReceiveProps
A: shouldComponentUpdate ---- false
Page: componentDidUpdate
注:这一点只是在当前React版本中生效
Currently, if shouldComponentUpdate() returns false, then componentWillUpdate(), render(), and componentDidUpdate() will not be invoked. Note that in the future React may treat shouldComponentUpdate() as a hint rather than a strict directive, and returning false
may still result in a re-rendering of the component.
—— React.Component: shouldComponentUpdate()
(3)如果组件Page
的shouldComponentUpdate
返回false
。
那么Page
组件的componentWillUpdate
render
componentDidUpdate
就都不执行了。
// 当前组件的shouldComponentUpdate为false
Page: componentWillMount
Page: render
A: componentWillMount
A: render
A: componentDidMount
Page: componentDidMount
Page: setState
Page: shouldComponentUpdate ---- false
注意,A
组件的componentWillReceiveProps
shouldComponentUpdate
componentWillUpdate
render
componentDidUpdate
也都不执行了。
因为,子组件的componentWillReceiveProps
是在父组件render
后执行的,子组件componentDidUpdate
后,父组件才会componentDidUpdate
。
2. DOM更新
在调试工具中查看哪些DOM被重新渲染
(1)打开chrome开发者工具
(2)按Esc
,打开console
(3)点击console左边的按钮,勾选Rendering
(4)勾选Paint Flashing
我们发现,即使render
函数被调用,DOM也不是全部更新,而是根据diff算法来更新。
3. 结论
只要执行this.setState
,则当前组件的shouldComponentUpdate
就会被调用。
如果当前组件的shouldComponentUpdate
返回true
,
则子组件的componentWillReceiveProps
shouldComponentUpdate
将被调用,不论子组件的props
是否被改变。
如果当前组件的shouldComponentUpdate
返回false
,
则子组件的componentWillReceiveProps
shouldComponentUpdate
componentWillUpdate
render
componentDidUpdate
都不被调用。
如果子组件的shouldComponentUpdate
返回true
,则调用componentWillUpdate
render
,然后通过diff算法更新DOM,最后调用componentDidUpdate
。
如果子组件的shouldComponentUpdate
返回false
,则子组件的componentWillUpdate
render
componentDidUpdate
都不被调用。
参考
React.Component: The Component Lifecycle
Reconciliation: The Diffing Algorithm