React 浅比较

21 阅读1分钟

以下 react 源码版本为 18.3.0

浅比较:用于判断“props 是否变了”,以便直接跳过组件渲染。

源码位置 18:50:packages/shared/shallowEqual.js

import is from './objectIs';
function shallowEqual(objA, objB) {
  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;
  for (let i = 0; i < keysA.length; i++) {
    const k = keysA[i];
    if (!hasOwnProperty.call(objB, k) || !is(objA[k], objB[k])) {
      return false;
    }
  }
  return true;
}

源码位置 packages/shared/objectIs.js

function is(x: any, y: any) {
    return (
        (x === y && (x !== 0 || 1 / x === 1 / y)) || (x !== x && y !== y) // eslint-disable-line no-self-compare
    );
}
  
const objectIs: (x: any, y: any) => boolean = typeof Object.is === 'function' ? Object.is : is;

export default objectIs;

(x !== 0 || 1 / x === 1 / y) 这段逻辑是在区分 +0 和 -0:

  • x === y 先判断“严格相等”。在 JS 里 +0 === -0 为 true。
  • (x !== 0 || 1 / x === 1 / y) 用来过滤掉 0 的特殊情况:
    • 如果 x 不是 0,直接通过。
    • 如果 x 是 0,就比较 1/x1/y1/+0 得到 +Infinity1/-0 得到 -Infinity。只有两者都为同号时才算相等,从而把 +0 和 -0 区分开。

React.memo 组件路径中,默认比较器是 shallowEqual;若比较为真且 ref 未变,则直接 bailout,不再渲染子树

549:555:packages/react-reconciler/src/ReactFiberBeginWork.new.js

const prevProps = currentChild.memoizedProps;
// Default to shallow comparison
let compare = Component.compare;
compare = compare !== null ? compare : shallowEqual;
if (compare(prevProps, nextProps) && current.ref === workInProgress.ref) {
  return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);
}

总结:

  • 浅比较:检查 props(和 ref)是否变化,若无变化可直接 bail out,避免组件及其子树重渲。