我们在React项目中经常会遇到渲染前后的props或state的比较,如果掌握React底层的比较原理,就可以更好的优化项目达到减少不必要的渲染,或者不出现与自己的想法不同的渲染结果。本文从4个角度来说明他们的比较逻辑:
PureComponent的willShouldUpdate与memo它们的默认比较逻辑hooks的比较逻辑Redux中比较逻辑immerjs的每个节点的比较逻辑
PureComponent的willShouldUpdate与memo它们的默认比较逻辑
它们的比较逻辑在官网的描述是‘浅层比较’,源码中是shallowEqual这个函数来进行的比较。
PureComponent的比较逻辑是:
// 注意不是每个属性进行shadowEqual而是props与prevProps和state与prevState进行比较。
!shallowEqual(oldProps, newProps) || !shallowEqual(oldState, newState)
memo的比较逻辑是:也是使用的shallowEqual只不过它只比较了前后的props
!shallowEqual(oldProps, newProps)
其中shallowEqual浅层比较函数:先使用Object.is函数进行比较,然后进行一层的比较,不会递归的进行深层次的比较。
// is就是Object.is的pollfiy
/**
* 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 {
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);
if (keysA.length !== keysB.length) {
return false;
}
// Test for A's keys different from B.
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;
}
hooks的比较逻辑
官网说使用了Object.is函数进行比较useState的set函数,useCallback的依赖数组,useMemo的依赖数组,useLayoutEffect的比较逻辑均与useEffect相同。
Redux中比较逻辑
reducer返回值是将整个store的前后引用进行浅比较也就是 ‘===’
selector的比较逻辑是浅比较 ‘===’
immerjs的每个节点的比较逻辑
比较逻辑的源码使用的是的Object.is的pollfiy,所以当produce函数中属性前后都是NaN的时候并不会生成新的对象引用
总结
| 所属框架 | 方法 | 比较逻辑 |
|---|---|---|
React | willShouldUpdate | 浅层比较shallowEqual |
React | memo | 浅层比较shallowEqual |
React | hooks的依赖数组 | Object.is |
react-redux | reducer的返回值与store | === |
react-redux | selector函数每次的返回值 | === |
immer.js | 每个节点比较 | Object.is |
参考
shadowEqual: React源码
useEffect比较逻辑:React官网