1

在renderRootSync执行完render相关阶段后,就会进入commit阶段。performSyncWorkOnRoot函数执行的末尾调用commitRoot(root);

commit阶段工作

在 rootFiber.firstEffect 上保存了一条需要执行副作用的 Fiber 节点的单向链表effectList,这些 Fiber 节点的 updateQueue 中保存了变化的 props。

这些副作用对应的 DOM 操作在commit 阶段执行。

除此之外,一些生命周期钩子(比如 componentDidXXX)、hook(比如 useEffect)需要在commit 阶段执行。

commit 阶段的主要工作分为三部分:

  • before mutation 阶段(执行 DOM 操作前)
  • mutation 阶段(执行 DOM 操作)
  • layout 阶段(执行 DOM 操作后)

commitRoot

commit阶段工作起点,把fiberNode传参进去

function commitRoot(root) {
  // getCurrentPriorityLevel调度优先级相关
  const renderPriorityLevel = getCurrentPriorityLevel();
  runWithPriority(
    ImmediateSchedulerPriority,
    commitRootImpl.bind(null, root, renderPriorityLevel),
  );
  return null;
}

commitRootImpl

代码300多行, 主要包括commit的前置工作和上文三阶段的执行,下面代码主要是前置部分

commit阶段前置工作:

  • 调用flushPassiveEffects执行完所有effect的任务
  • 初始化相关变量
  • 赋值firstEffect给后面遍历effectList用

    function commitRootImpl(root, renderPriorityLevel) {
    do {
      // 触发 useEffect 回调与其他同步任务。由于这些任务可能触发新的渲染,所以这里要一直遍历执行直到没有任务
      flushPassiveEffects();
    } while (rootWithPendingPassiveEffects !== null);
    
    // 指向当前应用的rootFiber
    const finishedWork = root.finishedWork;
    // 优先级
    const lanes = root.finishedLanes;
    if (finishedWork === null) {
      return null;
    }
    // 重置变量
    root.finishedWork = null;
    root.finishedLanes = NoLanes;
    
    // Scheduler回调函数重置
    root.callbackNode = null;
    root.callbackId = NoLanes;
    
    // 合并和清除优先级
    let remainingLanes = mergeLanes(finishedWork.lanes, finishedWork.childLanes);
    markRootFinished(root, remainingLanes);
    
    // 清除已完成的discrete updates,例如:用户鼠标点击触发的更新。
    if (rootsWithPendingDiscreteUpdates !== null) {
      if (
        !hasDiscreteLanes(remainingLanes) &&
        rootsWithPendingDiscreteUpdates.has(root)
      ) {
        rootsWithPendingDiscreteUpdates.delete(root);
      }
    }
    
    // 重置全局变量
    if (root === workInProgressRoot) {
      workInProgressRoot = null;
      workInProgress = null;
      workInProgressRootRenderLanes = NoLanes;
    } else {
    }
    
    
    // 将effectList副作用赋值给firstEffect
    let firstEffect;
    if (finishedWork.effectTag > PerformedWork) {
      if (finishedWork.lastEffect !== null) {
        finishedWork.lastEffect.nextEffect = finishedWork;
        firstEffect = finishedWork.firstEffect;
      } else {
        firstEffect = finishedWork;
      }
    } else {
      firstEffect = finishedWork.firstEffect;
    }
    }

flushPassiveEffects

先设置优先级然后调用flushPassiveEffectsImpl,执行所有effect的任务

function flushPassiveEffects() {
  if (pendingPassiveEffectsRenderPriority !== NoSchedulerPriority) {
    const priorityLevel =
      pendingPassiveEffectsRenderPriority > NormalSchedulerPriority
        ? NormalSchedulerPriority
        : pendingPassiveEffectsRenderPriority;
    pendingPassiveEffectsRenderPriority = NoSchedulerPriority;
    const previousLanePriority = getCurrentUpdateLanePriority();
    try {
      setCurrentUpdateLanePriority(
        schedulerPriorityToLanePriority(priorityLevel),
      );
      return runWithPriority(priorityLevel, flushPassiveEffectsImpl);
    } finally {
      setCurrentUpdateLanePriority(previousLanePriority);
    }
  }
}

总结

  1. 这一章主要看了commitRootImpl函数部分代码
  2. 了解commmit阶段的前置工作
  3. 了解到有三个不一样的函数执行流程:

    • before mutation 阶段(执行 DOM 操作前)
    • mutation 阶段(执行 DOM 操作)
    • layout 阶段(执行 DOM 操作后)

墨客的全栈之路
5 声望0 粉丝