Loading...
墨滴

早睡wqi

2021/09/25  阅读:26  主题:默认主题

React render阶段解析一

今天来讲一下render函数到render阶段的流程,render阶段和render函数不是同一个东西哦!
render阶段是react Fiber节点创建的阶段,也是我们diff算法执行的主要阶段。
render函数是挂载和渲染jsx节点的入口函数。

scheduleUpdateOnFiber

scheduleUpdateOnFiber函数断点 scheduleUpdateOnFiber函数从执行栈来看是在updateContainer函数中调用,主要是处理优先级和挂载更新节点

function scheduleUpdateOnFiber(
  fiber: Fiber,
  lane: Lane,
  eventTime: number,
{
  // 判断是否是无限嵌套的update队列,例如render函数里面放setState,最大的值是50
  checkForNestedUpdates();
  // 测试环境使用
  warnAboutRenderPhaseUpdatesInDEV(fiber);
  // 更新优先级,先忽略
  const root = markUpdateLaneFromFiberToRoot(fiber, lane);
  // ...

  if (lane === SyncLane) {
    if (
      // Check if we're inside unbatchedUpdates
      (executionContext & LegacyUnbatchedContext) !== NoContext &&
      // Check if we're not already rendering
      (executionContext & (RenderContext | CommitContext)) === NoContext
    ) {
      // Register pending interactions on the root to avoid losing traced interaction data.
      // 跟踪这些update,并计数、检测它们是否会报错
      schedulePendingInteractions(root, lane);

      // This is a legacy edge case. The initial mount of a ReactDOM.render-ed
      // root inside of batchedUpdates should be synchronous, but layout updates
      // should be deferred until the end of the batch.
      // 执行Fiber相关事情
      performSyncWorkOnRoot(root);
    } else {
      // 更新
  } else {
    ...
  }
  ...
  mostRecentlyUpdatedRoot = root;
}

performSyncWorkOnRoot

render阶段和commit阶段执行函数

function performSyncWorkOnRoot(root{
  invariant(
    (executionContext & (RenderContext | CommitContext)) === NoContext,
    'Should not already be working.',
  );
  // 处理 useEffect
  flushPassiveEffects();

  let lanes;
  let exitStatus;
  if (
    root === workInProgressRoot &&
    includesSomeLane(root.expiredLanes, workInProgressRootRenderLanes)
  ) {
    // 渲染过期节点
    lanes = workInProgressRootRenderLanes;
    exitStatus = renderRootSync(root, lanes);
    if (
      includesSomeLane(
        workInProgressRootIncludedLanes,
        workInProgressRootUpdatedLanes,
      )
    ) {
      // The render included lanes that were updated during the render phase.
      // For example, when unhiding a hidden tree, we include all the lanes
      // that were previously skipped when the tree was hidden. That set of
      // lanes is a superset of the lanes we started rendering with.
      //
      // Note that this only happens when part of the tree is rendered
      // concurrently. If the whole tree is rendered synchronously, then there
      // are no interleaved events.
      lanes = getNextLanes(root, lanes);
      exitStatus = renderRootSync(root, lanes);
    }
  } else {
    // 计算优先级
    lanes = getNextLanes(root, NoLanes);
    // render入口函数
    exitStatus = renderRootSync(root, lanes);
  }
  // ...
}

renderRootSync

render阶段执行函数,部分注释[1]

function renderRootSync(root: FiberRoot, lanes: Lanes{
  // 旧的上下文
  const prevExecutionContext = executionContext;
  // 切换到渲染执行上下文
  executionContext |= RenderContext;
  // 初始上下文为8 ,非批量同步: 8 , RenderContext: render: 16
  // hooks相关,返回hook相关函数
  const prevDispatcher = pushDispatcher(root);

  // If the root or lanes have changed, throw out the existing stack
  // and prepare a fresh one. Otherwise we'll continue where we left off.
  if (workInProgressRoot !== root || workInProgressRootRenderLanes !== lanes) {
    // 构建 workInProgressFiber 树及 rootFiber
    prepareFreshStack(root, lanes);
    // 初始渲染不执行 内部条件判断不成立
    startWorkOnPendingInteractions(root, lanes);
  }

  const prevInteractions = pushInteractions(root);

  do {
    try {
      // 循环处理workInProgress
      workLoopSync();
      break;
    } catch (thrownValue) {
      handleError(root, thrownValue);
    }
  } while (true);
  resetContextDependencies();
  if (enableSchedulerTracing) {
    popInteractions(((prevInteractions: any): Set<Interaction>));
  }

  executionContext = prevExecutionContext;
  popDispatcher(prevDispatcher);

  if (workInProgress !== null) {
    // This is a sync render, so we should have finished the whole tree.
    invariant(
      false,
      'Cannot commit an incomplete root. This error is likely caused by a ' +
        'bug in React. Please file an issue.',
    );
  }

  // Set this to null to indicate there's no in-progress render.
  workInProgressRoot = null;
  workInProgressRootRenderLanes = NoLanes;

  return workInProgressRootExitStatus;
}

workLoopSync

function workLoopSync({
  // Already timed out, so perform work without checking if we need to yield.
  while (workInProgress !== null) {
    performUnitOfWork(workInProgress);
  }
}

performUnitOfWork

function performUnitOfWork(unitOfWork: Fiber): void {
  // 获取current树
  const current = unitOfWork.alternate;
  // 开发环境忽略
  setCurrentDebugFiberInDEV(unitOfWork);

  let next;
  if (enableProfilerTimer && (unitOfWork.mode & ProfileMode) !== NoMode) {
    startProfilerTimer(unitOfWork);
    // beginWork入口函数
    next = beginWork(current, unitOfWork, subtreeRenderLanes);
    stopProfilerTimerIfRunningAndRecordDelta(unitOfWork, true);
  } else {
    next = beginWork(current, unitOfWork, subtreeRenderLanes);
  }

  resetCurrentDebugFiberInDEV();
  unitOfWork.memoizedProps = unitOfWork.pendingProps;
  if (next === null) {
    // completeWork入口
    completeUnitOfWork(unitOfWork);
  } else {
    workInProgress = next;
  }

  ReactCurrentOwner.current = null;
}

总结

本次主要是继render后的流程展示

  1. scheduler阶段的updateContatiner函数调用scheduleUpdateOnFiber函数
  2. 当调用 performSyncWorkOnRoot 方法就表示正式进入 render 阶段,在renderRootSync进行构建。
  3. 判断是否有 workInProgress Fiber 树,mount首次挂载没有就prepareFreshStack构建。
  4. workLoopSync主要循环处理workInProgress,在Concurrent模式下还有shouldYield判断条件可中断处理(这是后话)
  5. workLoopSync循环执行performUnitOfWork,performUnitOfWork是beginWork和completeWork入口。
  6. 下一章节主要讲beginWork执行流程敬请期待!
流程图
流程图

参考资料

[1]

react17源码: https://zhuanlan.zhihu.com/p/317635000。

早睡wqi

2021/09/25  阅读:26  主题:默认主题

作者介绍

早睡wqi