Skip to content
目录

Memo组件的具体一个渲染流程

react-memo

Memo使用方法

typescript
import React from 'react'

const MemoComponent = () => {
  return (
    <div>
      MemoComponent
    </div>
  )
}

export default React.memo(MemoComponent)

memo函数其实就是创建了一个ReactElement

memo

typescript
export default function memo<Props>(
  type: React$ElementType,
  compare?: (oldProps: Props, newProps: Props) => boolean,
) {
  return {
    $$typeof: REACT_MEMO_TYPE,
    type,
    compare: compare === undefined ? null : compare,
  };
}

updateMemoComponent

typescript
function updateMemoComponent(
  current: Fiber | null,
  workInProgress: Fiber,
  Component: any,
  nextProps: any,
  updateExpirationTime,
  renderExpirationTime: ExpirationTime,
): null | Fiber {
  if (current === null) {
    // Component 就是React.memo函数返回的那个ReactElement
    // type属性就是React.memo包裹的组件
    let type = Component.type;
    if (
      isSimpleFunctionComponent(type) &&
      Component.compare === null &&
      Component.defaultProps === undefined
    ) {
      // 如果是一个简单的memo函数组件,其实当前memo节点并不会有实质的dom
      // 节点的渲染,应该需要调和的是memo包裹的函数组件,所以在这里将type
      // 更新到memo的fiber对象上,也就是函数组件和memo函数组件就是公用一个Fiber
      workInProgress.tag = SimpleMemoComponent;
      workInProgress.type = type;
      return updateSimpleMemoComponent(
        current,
        workInProgress,
        type,
        nextProps,
        updateExpirationTime,
        renderExpirationTime,
      );
    }
    // 不符合上面的条件,直接创建Fiber
    let child = createFiberFromTypeAndProps(
      Component.type,
      null,
      nextProps,
      null,
      workInProgress.mode,
      renderExpirationTime,
    );
    child.ref = workInProgress.ref;
    child.return = workInProgress;
    workInProgress.child = child;
    return child;
  }
  let currentChild = ((current.child: any): Fiber); // This is always exactly one child
  if (updateExpirationTime < renderExpirationTime) {
    // updateExpirationTime < renderExpirationTime 说明当前函数组件
    // 渲染的优先级比较低,并不需要在这次更新中渲染,这个时候会先判断新老props
    // 如果没有改变, 那么提前在这一次渲染将这个组件的更新跳过
    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,
        renderExpirationTime,
      );
    }
  }
  // 重新渲染Fiber,进行更新
  let newChild = createWorkInProgress(
    currentChild,
    nextProps,
    renderExpirationTime,
  );
  newChild.ref = workInProgress.ref;
  newChild.return = workInProgress;
  workInProgress.child = newChild;
  return newChild;
}

updateSimpleMemoComponent

typescript
function updateSimpleMemoComponent(
  current: Fiber | null,
  workInProgress: Fiber,
  Component: any,
  nextProps: any,
  updateExpirationTime,
  renderExpirationTime: ExpirationTime,
): null | Fiber {
  if (current !== null) {
    // 说明不是第一次渲染,这个时候会对新老props进行浅比较
    // 如果浅比较相等,也就是新老props没有变化,那么会跳过当前组件的更新
    const prevProps = current.memoizedProps;
    if (
      shallowEqual(prevProps, nextProps) &&
      current.ref === workInProgress.ref
    ) {
      didReceiveUpdate = false;
      if (updateExpirationTime < renderExpirationTime) {
        return bailoutOnAlreadyFinishedWork(
          current,
          workInProgress,
          renderExpirationTime,
        );
      }
    }
  }
  // 否则的话,直接调和函数式组件
  return updateFunctionComponent(
    current,
    workInProgress,
    Component,
    nextProps,
    renderExpirationTime,
  );
}

shallowEqual

  • 首先使用is函数判断两个对象是否相等, 这里是严格比较
  • 如果不相等,处理null的情况, 两个null是不相等
  • 其次在针对两个对象做一个遍历,对比各自的属性是否相等(没有进行递归)
typescript
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
  );
}

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;
}

备案号: 浙ICP备2023000081号