why-did-you-update会比较组件的 state 和 props 的变化,从而发现
组件render方法不必要的调用。 #安装
npm install why-did-you-update --save-dev
# 使用
import React from 'react'
if (process.env.NODE_ENV !== 'production') {
const {whyDidYouUpdate} = require('why-did-you-update')
whyDidYouUpdate(React) }
React组件的生命周期方法中提供了一个shouldComponentUpdate方 法,这个方法的默认返回值是true,如果返回false,组件此次的更新将 会停止,也就是后续的componentWillUpdate、render等方法都不会再被 执行。我们可以把这个方法作为钩子,在这个方法中根据组件自身的业 务逻辑决定返回true还是false,从而避免组件不必要的渲染。例如,我 们通过比较props中的一个自定义属性item,决定是否需要继续组件的更 新过程,代码如下:
class MyComponent extend React.Component { shouldComponentUpdate(nextProps, nextState) {
if(nextProps.item === this.props.item) { return false;
}
return true; }
// ... }
注意,示例中对item的比较是通过===比较对象的引用,所以即使 两个对象的引用不相等,它们的内容也可能是相等的。最精确的比较方 式是遍历对象的每一层级的属性分别比较,也就是进行深比较(deep compare),但shouldComponentUpdate被频繁调用,如果props和state的 对象层级很深,深比较对性能的影响就比较大。一种折中的方案是,只 比较对象的第一层级的属性,也就是执行浅比较(shallow compare)。 例如下面两个对象:
const item = { foo, bar };
const nextItem = { foo, bar };
执行浅比较会使用===比较item.foo和nextItem.foo、item.bar和 nextItem.bar,而不会继续比较foo、bar的内容。React中提供了一个 PureComponent组件,这个组件会使用浅比较来比较新旧props和state, 因此可以通过让组件继承PureComponent来替代手写 shouldComponentUpdate的逻辑。但是,使用浅比较很容易因为直接修 改数据而产生错误,例如:
class NumberList extends React.PureComponent { constructor(props) {
super(props); this.state = {
numbers: [1,2,3,4] };
this.handleClick = this.handleClick.bind(this); }
// numbers中新加一个数值 handleClick() {
const numbers = this.state.numbers; // 直接修改numbers对象
numbers.push(numbers[numbers.length-1] + 1);
this.setState({numbers: numbers}); }
render() { return (
<div>
<button onClick={this.handleClick} /> {this.state.numbers.map(item => <div>{item}</div>)}
</div> );
} }
点击Button,NumberList并不会重新调用render,因为handleClick中 是直接修改this.state.numbers这个数组的,this.state.numbers的引用在 setState前后并没有发生改变,所以shouldComponentUpdate会返回 false,从而终止组件的更新过程。在第4章深入理解组件state中,我们 讲到要把state当作不可变对象,一个重要的原因就是为了提高组件state 比较的效率。对于不可变对象来说,只需要比较对象的引用就能判断 state是否发生改变。