原理
当组件更新时,如果组件的 props 和 state 都没发生改变, render 方法就不会触发,省去 Virtual DOM 的生成和比对过程,达到提升性能的目的。具体就是 React 自动帮我们做了一层浅比较:
if (this._compositeType === CompositeTypes.PureClass) {
shouldUpdate = !shallowEqual(prevProps, nextProps)
|| !shallowEqual(inst.state, nextState);
}
shallowEqual浅比较
- 通过
Object.is
判断是否是同一个对象 - Object.keys(state | props) 的长度是否一致
- 每一个 key 是否两者都有
/**
* Performs equality by iterating through keys on an object and returning false
* when any key has values which are not strictly equal between the arguments.
* Returns true when the values of all keys are strictly equal.
*/
function shallowEqual(objA: mixed, objB: mixed): boolean {
/**
* Object.is(https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/is)
* Object.is() 判断两个值是否相同。如果下列任何一项成立,则两个值相同:
两个值都是 undefined
两个值都是 null
两个值都是 true 或者都是 false
两个值是由相同个数的字符按照相同的顺序组成的字符串
两个值指向同一个对象
两个值都是数字并且
都是正零 +0
都是负零 -0
都是 NaN
都是除零和 NaN 外的其它同一个数字
*/
if (is(objA, objB)) {
return true;
}
if (
typeof objA !== 'object' ||
objA === null ||
typeof objB !== 'object' ||
objB === null
) {
return false;
}
const keysA = Object.keys(objA);
const keysB = Object.keys(objB);
// 判断两个对象中的key的数量是否相同
if (keysA.length !== keysB.length) {
return false;
}
// 判断两个对象中的key和value是否相同
for (let i = 0; i < keysA.length; i++) {
if (
!hasOwnProperty.call(objB, keysA[i]) ||
!is(objA[keysA[i]], objB[keysA[i]])
) {
return false;
}
}
return true;
}
适用于展示型组件
易变数据不能使用一个引用
handleClick = () => {
const { items } = this.state;
items.pop();
this.setState({ items });
}
如果是Component,点击delete会得到li的减少
如果是PureComponent,li不会改变,因为items用的是一个引用,所以shallowEqual结果是true。
可以改正为
handleClick = () =>{
const {items} =this.state
items.pop()
this.setState({items:[].concat(items)})
}
与 shouldComponentUpdate 共存
如果 PureComponent 里有 shouldComponentUpdate 函数的话,直接使用 shouldComponentUpdate 的结果作为是否更新的依据,没有 shouldComponentUpdate 函数的话,才会去判断是不是 PureComponent ,是的话再去做 shallowEqual 浅比较。
// 这个变量用来控制组件是否需要更新
var shouldUpdate = true;
// inst 是组件实例
if (inst.shouldComponentUpdate) {
shouldUpdate = inst.shouldComponentUpdate(nextProps, nextState, nextContext);
} else {
if (this._compositeType === CompositeType.PureClass) {
shouldUpdate = !shallowEqual(prevProps, nextProps) ||
!shallowEqual(inst.state, nextState);
}
}