React18 阅读笔记 -- commit

71 阅读4分钟

commitRoot

beginWork 和 completeWork 阶段都正常结束后,此时所有的 fiber 和真实节点创建完成,进入到 commit 阶段:

finishConcurrentRender(root, exitStatus, lanes) {
 switch (exitStatus) {
  case RootCompleted: {
    // The work completed. Ready to commit.
      commitRoot(
        root,
        workInProgressRootRecoverableErrors,
        workInProgressTransitions,
      )
      break;
  }
 }
}

function commitRoot(root) {
  const previousUpdateLanePriority = getCurrentUpdatePriority();
  const prevTransition = ReactCurrentBatchConfig.transition;
  try {
    ReactCurrentBatchConfig.transition = 0;
    // 优先级为同步,不可被打断
    setCurrentUpdatePriority(DiscreteEventPriority);
    commitRootImpl(root, previousUpdateLanePriority);
  } finally {
    ReactCurrentBatchConfig.transition = prevTransition;
    setCurrentUpdatePriority(previousUpdateLanePriority);
  }

  return null;
}

commitRootImpl

具体执行方法,先检测是否有上轮更新未执行的 effect,如果有则先调用 flushPassiveEffects 同步执行上轮遗留 effect 任务。 否则通过 scheduleCallback 将 本轮 effect 加入到 任务队列(taskQueue)中。

function commitRootImpl(
  root: FiberRoot,
  recoverableErrors: null | Array<CapturedValue<mixed>>,
  transitions: Array<Transition> | null,
  renderPriorityLevel: EventPriority
) {
  do {
    flushPassiveEffects();
  } while (rootWithPendingPassiveEffects !== null);

  const finishedWork = root.finishedWork;
  const lanes = root.finishedLanes;

  root.finishedWork = null;
  root.finishedLanes = NoLanes;
  root.callbackNode = null;
  root.callbackPriority = NoLane;

  // 如果flags/subtreeFlags中存在PassiveMask,即Passive|ChildDeletion,
  // 那么 rootDoesHavePassiveEffects 为true。也就是说如果使用了useEffect或者是节点有删除的情况
  // 那么就会执行flushPassiveEffects方法:
  if (
    (finishedWork.subtreeFlags & PassiveMask) !== NoFlags ||
    (finishedWork.flags & PassiveMask) !== NoFlags
  ) {
    if (!rootDoesHavePassiveEffects) {
      rootDoesHavePassiveEffects = true;
      // 异步执行 useEffect
      scheduleCallback(NormalSchedulerPriority, () => {
        flushPassiveEffects();
        return null;
      });
    }
  }

  // 如果subtreeHasEffects或rootHasEffect存在,说明有更新。
  // 首先会进入beforeMutation阶段,调用commitBeforeMutationEffects方法:
  if (subtreeHasEffects || rootHasEffect) {
    const shouldFireAfterActiveInstanceBlur = commitBeforeMutationEffects(
      root,
      finishedWork
    );
  }

  commitMutationEffects(root, finishedWork, lanes);

  // 同步执行 useLayoutEffect 的 create 并存储 destroy
  commitLayoutEffects(finishedWork, root, lanes);

  if (rootDoesHavePassiveEffects) {
    rootDoesHavePassiveEffects = false;
    rootWithPendingPassiveEffects = root;
    pendingPassiveEffectsLanes = lanes;
  }

  root.current = finishedWork;

  // 如果还有未完成的更新,即优先级不够的更新,那么这里会被继续调度进行更新。
  ensureRootIsScheduled(root, now());

  // 如果当前更新中包含useEffect,并且lanes中含有同步lane,那么需要立即执行flushPassiveEffect
  // 相比于schedule执行flushPassiveEffects,这里执行更靠前。
  if (
    includesSomeLane(pendingPassiveEffectsLanes, SyncLane) &&
    root.tag !== LegacyRoot
  ) {
    flushPassiveEffects();
  }

  flushSyncCallbacks();

  return null;
}

// flushPassiveEffects

export function flushPassiveEffects(): boolean {
  // 如果 rootWithPendingPassiveEffects 存在,说明使用了 useEffect 或者有子节点被删除
  if (rootWithPendingPassiveEffects !== null) {
    const renderPriority = lanesToEventPriority(pendingPassiveEffectsLanes);
    const priority = lowerEventPriority(DefaultEventPriority, renderPriority);
    const prevTransition = ReactCurrentBatchConfig.transition;
    const previousPriority = getCurrentUpdatePriority();
    try {
      // transition 置为 0
      ReactCurrentBatchConfig.transition = 0;
      // 设置 update 优先级,获取 lane 的时候会用得到
      setCurrentUpdatePriority(priority);
      return flushPassiveEffectsImpl();
    } finally {
      setCurrentUpdatePriority(previousPriority);
      ReactCurrentBatchConfig.transition = prevTransition;
    }
  }
  return false;
}

function flushPassiveEffectsImpl() {
  if (rootWithPendingPassiveEffects === null) {
    return false;
  }
  // 针对 fiber.flags === ChildDeletion 的 节点 执行
  // commitPassiveUnmountInsideDeletedTreeOnFiber(fiber, nearestMountedAncestor);
  // 对 function 组件 通过调用
  // safelyCallDestroy(finishedWork, nearestMountedAncestor, destroy);
  // 方法 执行 destroy。然后在
  // commitPassiveUnmountEffectsInsideOfDeletedTree_complete
  // 方法中删除fiber引用并且删除对应真实节点的引用
  // 到底后 执行 commitPassiveUnmountInsideDeletedTreeOnFiber 自下而上处理兄弟节点
  commitPassiveUnmountEffects(root.current);
  // 同上步骤 调用effects里的 create 方法并生成destroy
  commitPassiveMountEffects(root, root.current, lanes, transitions);

  flushSyncCallbacks();
}

BeforeMutationEffects

// BeforeMutationEffects 阶段
export function commitBeforeMutationEffects(
  root: FiberRoot,
  firstChild: Fiber
) {
  focusedInstanceHandle = prepareForCommit(root.containerInfo);
  nextEffect = firstChild;
  commitBeforeMutationEffects_begin();

  // 。。。省略部分代码
}

function commitBeforeMutationEffects_begin() {
  // 向下遍历,直到找到一个不符合BeforeMutationMask的节点
  while (nextEffect !== null) {
    const fiber = nextEffect;

    const child = fiber.child;
    // export const BeforeMutationMask = Update | Snapshot
    if (
      (fiber.subtreeFlags & BeforeMutationMask) !== NoFlags &&
      child !== null
    ) {
      child.return = fiber;
      nextEffect = child;
    } else {
      commitBeforeMutationEffects_complete();
    }
  }
}

// 自下往上遍历,执行 commitBeforeMutationEffectsOnFiber
function commitBeforeMutationEffects_complete() {
  while (nextEffect !== null) {
    const fiber = nextEffect;
    // 处理 class组件中定义的getSnapshotBeforeUpdate函数
    // 或者 处理 current === null || current.child === null 的HostRoot
    // 将 root.containerInfo 真实节点的内容置空
    commitBeforeMutationEffectsOnFiber(fiber);

    const sibling = fiber.sibling;
    if (sibling !== null) {
      sibling.return = fiber.return;
      nextEffect = sibling;
      return;
    }
    nextEffect = fiber.return;
  }
}

mutation

export function commitMutationEffects(
  root: FiberRoot,
  finishedWork: Fiber,
  committedLanes: Lanes
) {
  inProgressLanes = committedLanes;
  inProgressRoot = root;

  // mutation阶段的执行顺序也是自上而下开始遍历,执行删除dom操作,然后到了最底层子节点,
  // 开始自下而上执行插入和更新dom操作
  commitMutationEffectsOnFiber(finishedWork, root, committedLanes);

  inProgressLanes = null;
  inProgressRoot = null;
}

function commitMutationEffectsOnFiber(
  finishedWork: Fiber,
  root: FiberRoot,
  lanes: Lanes
) {
    const current = finishedWork.alternate;
  const flags = finishedWork.flags;

  // The effect flag should be checked *after* we refine the type of fiber,
  // because the fiber tag is more specific. An exception is any flag related
  // to reconcilation, because those can be set on all fiber types.
  switch (finishedWork.tag) {
    case FunctionComponent:
    case ForwardRef:
    case MemoComponent:
    case SimpleMemoComponent: {
      // Deletion 深度遍历执行删除操作
      recursivelyTraverseMutationEffects(root, finishedWork, lanes);
      // Placement 插入逻辑
      commitReconciliationEffects(finishedWork);
      if (flags & Update) {
        // 找出 useInsertionEffect 的 destroy 方法去调用
        // 需要注意 destroy 可能为 undefined(函数组件初次挂载的情况下)
          commitHookEffectListUnmount(
            HookInsertion | HookHasEffect,
            finishedWork,
            finishedWork.return,
          );
          // 执行 useInsertionEffect 的回调函数,并将返回值保存到 effect.destory 里。
          commitHookEffectListMount(
            HookInsertion | HookHasEffect,
            finishedWork,
          );
        if (
          enableProfilerTimer &&
          enableProfilerCommitHooks &&
          finishedWork.mode & ProfileMode
        ) {
            startLayoutEffectTimer();
            // useLayoutEffect 对应的 destroy 方法
            // 同样可能不存在
            commitHookEffectListUnmount(
              HookLayout | HookHasEffect,
              finishedWork,
              finishedWork.return,
            );
          recordLayoutEffectDuration(finishedWork);
        }
      }
      return;
    }
    case HostComponent: {
      recursivelyTraverseMutationEffects(root, finishedWork, lanes);
      commitReconciliationEffects(finishedWork);
    }
}

流程图

附commit 流程图(非原作者,出处遗忘...) completeWork.webp