React 源码系列:react 的 updateContainer 做了啥

934 阅读3分钟

「这是我参与11月更文挑战的第19天,活动详情查看:2021最后一次更文挑战

updateContainer 承载的逻辑是将数据更新后的 Fiber 更新到 dom 容器上,那么这个过程做了些什么呢?

updateContainer

export function updateContainer (
  element: ReactNodeList,
  container: OpaqueRoot,
  parentComponent: ?React$Component<any, any>,
  callback: ?Function,
): Lane {
//...
}

主要逻辑如下:

  1. 获取 update 的当前时间戳 eventTime = requestEventTime()
  2. lane = requestUpdateLane(current)
  3. 从父组件中获取 context:getContextForSubtree(...)挂载到当前 container 的 context 字段或 pendingContext 字段
  4. 根据 lane 和 eventTime 创建 update 对象,将 element 挂载到 update.payload.element 上,将 callback 挂载在 update.callback
  5. scheduleUpdateOnFiber 函数,返回值传给entangleTransition函数

requestUpdateLane

lane 中文含义是车道, 在 React 中,lane 是一个number值,使用 32 个比特位表示32个“车道”。在源码的 packages/react-reconciler/src/ReactFiberLane.new.js中定义了很多不同类型的 lane 值,分类如下:

  • TotalLanes:所有车道
  • NoLanes / NoLane: 无可用车道
  • SyncLane:同步车道
  • InputContinuousHydrationLane:连续输入Hydration 车道
  • InputContinuousLane:连续输入车道
  • DefaultHydrationLane: 默认Hydration车道
  • DefaultLane: 默认车道
  • TransitionHydrationLane: 过渡Hydration车道
  • TransitionLanes: 过渡车道组,包含 TransitionLane1 ~ TransitionLane16
  • RetryLanes:重试车道组,包含RetryLane1~RetryLane5,其中 RetryLane1 又称为 SomeRetryLane
  • SelectiveHydrationLane:选择性的 Hydration 车道
  • IdleLane: 闲置车道
  • IdleHydrationLane: 闲置Hydration车道
  • NonIdleLanes:除IdleLane和IdleHydrationLane以外的所有车道
  • OffscreenLane:离屏车道

要了解 requestUpdateLane,还需要了解一个叫 ExecutionContext 的概念。ExecutionContext 中文是执行上下文,在react 中也是一个 number 值,react 使用低 4 位的比特位表示不同状态, 如下:

  • NoContext: 无上下文
  • BatchedContext:批处理上下文
  • RenderContext:渲染上下文
  • CommitContext:提交上下文
  • RetryAfterError:错误后重试上下文

requestUpdateLane 的作用就是按一定的优先级申请车道,逻辑如下:

  1. 如果是 fiber.mode 不是ConcurrentMode,则返回 SyncLane。(fiber.mode 的可能取值有ConcurrentModeProfileModeDebugTracingModeStrictLegacyModeStrictEffectsModeConcurrentUpdatesByDefaultMode
  2. 如果不是处于渲染上下文,且workInProgressRootRenderLanes 存在可用车道,有没有将渲染阶段更新延迟到下一个批处理(deferRenderPhaseUpdateToNextBatch = false), 则返回一个 pickArbitraryLane(workInProgressRootRenderLanes), 这个pickArbitraryLane是一个兜底行为,未来可能移除。
  3. 如果是一个过渡(requestCurrentTransition() !== NoTransition) ,则返回currentEventTransitionLane,如果 currentEventTransitionLane 无可用车道,则调claimNextTransitionLane
  4. 调用 getCurrentUpdatePriority 获取一个 updateLane,如果不是 NoLane,则返回它
  5. 返回 getCurrentEventPriority 得到的 eventLane

scheduleUpdateOnFiber(fiber, lane, eventTime)

  1. 调用 markUpdateLaneFromFiberToRoot(fiber, lane),如果返回值为null,则结束
  2. 标记 root 有一个 pending update
  3. 如果执行上下文是渲染上下文,且 root 是 workInProgressRoot,则更新 workInProgressRootRenderPhaseUpdatedLanes ,追踪在渲染阶段被更新的车道否则执行 4,5, 6
  4. 处理一段跟 ProfilerMode 相关的逻辑
  5. 当 root 是 workInProgressRoot 时,如果渲染阶段更新延迟到下一个批处理或者执行上下文不是渲染上下文,则更新 workInProgressRootInterleavedUpdatedLanes 的值
  6. 如果 workInProgressRootExitStatus === RootSuspendedWithDelay),意味着有了新的update进来,需要将当前的update标记为暂停,中断当前的渲染,切换到新的update
  7. 执行 ensureRootIsScheduled(root, eventTime);
  8. 再来一段与 flushSyncCallbacksOnlyInLegacyMode有关的代码,这是为了保留历史表现

entangleTransitions(root, fiber, lane)

  1. 如果 lane 是过渡车道(TransitionLanes),则取 root.pendingLanes root.updateQueue.shared.lanes的交集,更新到后者
  2. 执行 markRootEntangled