React中的比较原理

1,021 阅读2分钟

我们在React项目中经常会遇到渲染前后的propsstate的比较,如果掌握React底层的比较原理,就可以更好的优化项目达到减少不必要的渲染,或者不出现与自己的想法不同的渲染结果。本文从4个角度来说明他们的比较逻辑:

  1. PureComponentwillShouldUpdatememo它们的默认比较逻辑
  2. hooks的比较逻辑
  3. Redux中比较逻辑
  4. 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函数进行比较useStateset函数,useCallback的依赖数组,useMemo的依赖数组,useLayoutEffect的比较逻辑均与useEffect相同。

Redux中比较逻辑

reducer返回值是将整个store的前后引用进行浅比较也就是 ‘===’

selector的比较逻辑是浅比较 ‘===’

官网参考

immerjs的每个节点的比较逻辑

比较逻辑的源码使用的是的Object.is的pollfiy,所以当produce函数中属性前后都是NaN的时候并不会生成新的对象引用

总结

所属框架方法比较逻辑
ReactwillShouldUpdate浅层比较shallowEqual
Reactmemo浅层比较shallowEqual
Reacthooks的依赖数组Object.is
react-reduxreducer的返回值与store===
react-reduxselector函数每次的返回值===
immer.js每个节点比较Object.is

参考

shadowEqual: React源码

useEffect比较逻辑:React官网