React18.2源码解析之commit阶段(上)

424 阅读3分钟

记录学习过程,如有错误欢迎指出

开始之前(必看)

React version 18.2.0

DEV代码可以忽略,下面正文我使用省略号就代表是dev相关的代码,包含hydrate字样的也请忽略,那是服务端渲染相关,望周知

我使用深度优先(🐶)的方式讲解:即遇到函数先进入函数,执行完函数后,又退回之前的函数.而不是在一个函数中讲完了再讲函数中执行的函数(希望能听懂我在说什么^v^)

因为使用了深度优先的方式讲解,耦合比较重,不建议跳着看

commitRoot

commitRoot为sync模式下的commit,commit阶段内又分为3个小阶段

  1. before mutation
  2. mutation
  3. layout
function commitRoot(
  root: FiberRoot,
  recoverableErrors: null | Array<CapturedValue<mixed>>,
  transitions: Array<Transition> | null,
) {

  // 保存当前更新优先级
  const previousUpdateLanePriority = getCurrentUpdatePriority();
  const prevTransition = ReactCurrentBatchConfig.transition;

  try {
    ReactCurrentBatchConfig.transition = null;
    // 将当前优先级设为离散,为同步优先级,不可被打断
    // 并且DiscreteEventPriority是最高优先级任务
    setCurrentUpdatePriority(DiscreteEventPriority);
    
    //进入commitRootImpl
    commitRootImpl(
      root,
      recoverableErrors,
      transitions,
      previousUpdateLanePriority,
    );
  } finally {
    // 恢复transition
    ReactCurrentBatchConfig.transition = prevTransition;
    // 恢复优先级
    setCurrentUpdatePriority(previousUpdateLanePriority);
  }

  return null;
}

commitRootImpl(未完)

function commitRootImpl(
  root: FiberRoot,
  recoverableErrors: null | Array<CapturedValue<mixed>>,
  transitions: Array<Transition> | null,
  renderPriorityLevel: EventPriority,
) {
  // 采用 do while 的作用是,在 useEffect 内部
  // 可能会触发新的更新,新的更新可能会触发新的副作用 ,因此需要不断的循环,直到 为 null
  // 这一步是为了看看还有没有 没有执行的 useEffect, 有的话先执行他们
  do {
    flushPassiveEffects();
  } while (rootWithPendingPassiveEffects !== null);
  flushRenderPhaseStrictModeWarningsInDEV();
  
  
  
  // finishedWork就是alternate
  const finishedWork = root.finishedWork;
  // 优先级相关
  const lanes = root.finishedLanes;


  if (finishedWork === null) {
    ...
    
    if (enableSchedulingProfiler) {
      markCommitStopped();
    }

    return null;
  }
  
  
  root.finishedWork = null;
  root.finishedLanes = NoLanes;

  if (finishedWork === root.current) {
    throw new Error(
      // 无法提交与之前相同的树。这个错误可能是由React的一个bug引起的。请提交一个问题。
      'Cannot commit the same tree as before. This error is likely caused by ' +
        'a bug in React. Please file an issue.',
    );
  }

  // commitRoot绝不会返回一个continuation;它总是同步完成的。
  // 所以我们现在可以清除这些,以允许安排一个新的回调
  // 清空回调
  root.callbackNode = null;
  root.callbackPriority = NoLane;

  // 检查哪些车道上不再有任何工作安排,并将这些车道标记为已完成
  let remainingLanes = mergeLanes(finishedWork.lanes, finishedWork.childLanes);

  // 请确保考虑到在渲染阶段`被并发事件更新`的车道
  // 在渲染阶段,不要把它们标记为完成。
  // 这一步主要就是校验是否有被并发更新的车道,然后将并发更新的车道合并
  const concurrentlyUpdatedLanes = getConcurrentlyUpdatedLanes();
  remainingLanes = mergeLanes(remainingLanes, concurrentlyUpdatedLanes);

  //将remainingLanes中的所有车道标记已完成
  markRootFinished(root, remainingLanes);

  // 处理光标,重置一些 render 阶段使用的变量
  if (root === workInProgressRoot) {
    // 我们现在可以重新设置这些,因为它们已经完成了。
    workInProgressRoot = null;
    workInProgress = null;
    workInProgressRootRenderLanes = NoLanes;
  } else {
  }
  
  
  // 如果flags/subtreeFlags中存在PassiveMask,即Passive|ChildDeletion
  // 就是 调度 useEffect
  if (
    (finishedWork.subtreeFlags & PassiveMask) !== NoFlags ||
    (finishedWork.flags & PassiveMask) !== NoFlags
  ) {
    if (!rootDoesHavePassiveEffects) {
      // 也就是说如果使用了useEffect或者是节点有删除的情况
      rootDoesHavePassiveEffects = true;
      pendingPassiveEffectsRemainingLanes = remainingLanes;
      pendingPassiveTransitions = transitions;
      scheduleCallback(NormalSchedulerPriority, () => {
        // 执行flushPassiveEffects() 就是触发 useEffect
        flushPassiveEffects();
        return null;
      });
    }
  }
  
  
  // 子树是否有更新
  const subtreeHasEffects =
    (finishedWork.subtreeFlags &
      (BeforeMutationMask | MutationMask | LayoutMask | PassiveMask)) !==
    NoFlags;
  // root是否有effect
  const rootHasEffect =
    (finishedWork.flags &
      (BeforeMutationMask | MutationMask | LayoutMask | PassiveMask)) !==
    NoFlags;
    
    
  // 存在effect的情况
  if (subtreeHasEffects || rootHasEffect) {
    // 存在副作用,处理 Fiber 上的副作用
    const prevTransition = ReactCurrentBatchConfig.transition;
    ReactCurrentBatchConfig.transition = null;
    const previousPriority = getCurrentUpdatePriority();
    setCurrentUpdatePriority(DiscreteEventPriority);

    const prevExecutionContext = executionContext;
    executionContext |= CommitContext;

    // 在调用生命周期之前,将其重置为空
    ReactCurrentOwner.current = null;

    /**
     * before mutation阶段
     */


    // 第一个阶段是 before mutation ,在这个阶段可以读取改变之前的的 state
    // 生命周期函数 getSnapshotBeforeUpdate 的调用
    
    // 进入commitBeforeMutationEffects
    const shouldFireAfterActiveInstanceBlur = commitBeforeMutationEffects(
      root,
      finishedWork,
    );
    
    
    ....

commitBeforeMutationEffects

export function commitBeforeMutationEffects(
  root: FiberRoot,
  firstChild: Fiber,
) {
  // prepareForCommit() 返回null 并且初始化全局变量
  focusedInstanceHandle = prepareForCommit(root.containerInfo);

  nextEffect = firstChild;
  // 开始处理副作用
  commitBeforeMutationEffects_begin();

  // 不再跟踪fiber节点
  const shouldFire = shouldFireAfterActiveInstanceBlur;
  shouldFireAfterActiveInstanceBlur = false;
  focusedInstanceHandle = null;

  return shouldFire;
}

commitBeforeMutationEffects_begin

function commitBeforeMutationEffects_begin() {
  // 从上往下遍历,找到最底部并且有标记了 before mutation 的 fiber 节点
  while (nextEffect !== null) {
    const fiber = nextEffect;

    //这个阶段只用于beforeActiveInstanceBlur.如果它是关闭的,就跳过
    if (enableCreateEventHandleAPI) {
      // 如果节点标记了deletions,将会被删除,并且
      // 调用commitBeforeMutationEffectsDeletion创建 blur 事件并进行派发
      const deletions = fiber.deletions;
      if (deletions !== null) {
        for (let i = 0; i < deletions.length; i++) {
          const deletion = deletions[i];
          commitBeforeMutationEffectsDeletion(deletion);
        }
      }
    }

    const child = fiber.child;
    // 判断节点是否标记了 before mutation
    if (
      // 如果子树有包含BeforeMutationMask标记的节点
      (fiber.subtreeFlags & BeforeMutationMask) !== NoFlags &&
      child !== null// child !== null判断是否是最底部节点
    ) {
      // 向下继续获取
      child.return = fiber;
      nextEffect = child;
    } else {
      // 如果执行到这里,说明这个节点是标记了before mutation的`最底部节点`
      // 执行complete,完成更新fiber节点的 props 和 state
      commitBeforeMutationEffects_complete();
    }
  }
}

commitBeforeMutationEffects_complete

function commitBeforeMutationEffects_complete() {
  // 从下至上,归并
  while (nextEffect !== null) {
    const fiber = nextEffect;
    setCurrentDebugFiberInDEV(fiber);
    try {
      // 依次执行
      commitBeforeMutationEffectsOnFiber(fiber);
    } catch (error) {
      captureCommitPhaseError(fiber, fiber.return, error);
    }
    resetCurrentDebugFiberInDEV();

    // 判断是否存在兄弟节点
    const sibling = fiber.sibling;
    if (sibling !== null) {
      sibling.return = fiber.return;
      nextEffect = sibling;
      return;
    }

    // 向上获取
    nextEffect = fiber.return;
  }
}

commitBeforeMutationEffectsOnFiber

function commitBeforeMutationEffectsOnFiber(finishedWork: Fiber) {
  const current = finishedWork.alternate;
  const flags = finishedWork.flags;

  if (enableCreateEventHandleAPI) {
    if (!shouldFireAfterActiveInstanceBlur && focusedInstanceHandle !== null) {
      // Check to see if the focused element was inside of a hidden (Suspense) subtree.
      // TODO: Move this out of the hot path using a dedicated effect tag.
      if (
        finishedWork.tag === SuspenseComponent &&
        isSuspenseBoundaryBeingHidden(current, finishedWork) &&
        doesFiberContain(finishedWork, focusedInstanceHandle)
      ) {
        shouldFireAfterActiveInstanceBlur = true;
        beforeActiveInstanceBlur(finishedWork);
      }
    }
  }

  if ((flags & Snapshot) !== NoFlags) {
    setCurrentDebugFiberInDEV(finishedWork);

    // 根据 tag 的不同进入不同的处理逻辑
    switch (finishedWork.tag) {
      case FunctionComponent:
      case ForwardRef:
      case SimpleMemoComponent: {
        break;
      }
      
      case  ....
      .....

总结

可以看到在before mutation时,其内部逻辑和render阶段的beginWork和completeWork是一样的,先

记录学习过程,如有错误欢迎指出