React 协调器 render 阶段

先来看个全局变量 executionContext

executionContext

executionContext 标记当前执行环境,比如当前是 render 阶段或者 commit 阶段。

类型是 number,取值有 4 种:

js 复制代码
type ExecutionContext = number;

export const NoContext = /*             */ 0b000;
const BatchedContext = /*     批量/同步更新          */ 0b001;
export const RenderContext = /*    render 阶段     */ 0b010;
export const CommitContext = /*   commit 阶段      */ 0b100;

初始值为 NoContext

js 复制代码
// 参考 react/packages/react-reconciler/src/ReactFiberWorkLoop.js
let executionContext: ExecutionContext = NoContext; 

两个阶段

和 render 阶段平行的还有个 commit 阶段。

  1. render 阶段,构建 fiber 树 VDOM
  2. commit 阶段,将 VDOM 转换成 DOM

两者入口如下:

js 复制代码
// 这是每个并发任务的入口点,即通过 Scheduler 的所有内容
function performConcurrentWorkOnRoot(
  root: FiberRoot,
  didTimeout: boolean,
): RenderTaskFn | null {
  let lanes = getNextLanes(
    root,
    root === workInProgressRoot ? workInProgressRootRenderLanes : NoLanes,
  );
  // 没有更新
  if (lanes === NoLanes) {
    // Defensive coding. This is never expected to happen.
    return null;
  }

  // 在某些情况下,我们会禁用时间切片:如果 work 过长时间做计算(为了防止饥饿而将其视为"过期"的 work),或者我们处于默认启用同步更新模式。
  const shouldTimeSlice =
    !includesBlockingLane(root, lanes) &&
    !includesExpiredLane(root, lanes) &&
    (disableSchedulerTimeoutInWorkLoop || !didTimeout);

  let exitStatus = shouldTimeSlice
    ? renderRootConcurrent(root, lanes) // 使用时间切片
    // 1. render, 构建fiber树VDOM(beginWork|completeWork)
    : renderRootSync(root, lanes); // 不用时间切片

  // 2. commit, VDOM->DOM
  commitRoot(root);
}

renderRootSync

renderRootSync 是 render 阶段入口,包含 4 个步骤:

  1. render 阶段开始
  2. workInProgressTransitions 赋值
  3. 初始化
  4. 遍历构建 fiber 树

详细代码如下:

js 复制代码
function renderRootSync(root: FiberRoot) {
  // 1. render阶段开始
  const prevExecutionContext = executionContext;
  executionContext |= RenderContext; // 注意这里不能用等号,如果使用等号,会覆盖其他 execution,比如如果是批量更新,如果直接覆盖,批量更新就没有了
  // 2. workInProgressTransitions 赋值
  workInProgressTransitions = getTransitionsForLanes(root, lanes);
  // 3. 初始化
  prepareFreshStack(root, lanes);

  // 4. 遍历构建 fiber 树
  workLoopSync();

  // 重置 Context 的相关值,入 currentlyRenderingFiber 等
  resetContextDependencies();

  // 4. render结束,重置 executionContext
  executionContext = prevExecutionContext;
  
  workInProgressRoot = null; // 当前正在工作的 root
  workInProgressRootRenderLanes = NoLanes;

  // 防止有新的更新,再遍历一边更新队列
  finishQueueingConcurrentUpdates();

  return workInProgressRootExitStatus;
}

function prepareFreshStack(root: FiberRoot): Fiber {
  root.finishedWork = null;

  workInProgressRoot = root; // FiberRoot
  const rootWorkInProgress = createWorkInProgress(root.current, null); // Fiber
  if (workInProgress === null) {
    workInProgress = rootWorkInProgress; // Fiber
  }

  // 把 concurrentQueues 的内容添加到 fiber 的 queue 中,即给 fiber 的 lanes、childLanes 赋值
  finishQueueingConcurrentUpdates();

  return rootWorkInProgress;
}

function workLoopSync() {
  while (workInProgress !== null) {
    performUnitOfWork(workInProgress);
  }
}

function performUnitOfWork(unitOfWork: Fiber) {
  const current = unitOfWork.alternate;
  // 1. beginWork
  // 更新当前 fiber,比如 props/state 更新,生命周期函数执行、Hooks 函数执行等。
  // 返回下一个 fiber。
  let next = beginWork(current, unitOfWork);
  //  把 pendingProps 更新到 memoizedProps
  unitOfWork.memoizedProps = unitOfWork.pendingProps;
  // 1.1 执行自己
  // 1.2 (协调,bailout)返回子节点

  if (next === null) {
    // 没有产生新的work
    // 如果不再产生新的 work,那么当前 work 结束
    // 2. completeWork,比如生成原生节点,放在 fiber 的 stateNode 属性上
    completeUnitOfWork(unitOfWork);
  } else {
    workInProgress = next;
  }

  // 当前 Fiber 为 null
  ReactCurrentOwner.current = null
}

// 深度优先遍历,子节点、兄弟节点、叔叔节点、爷爷的兄弟节点...(王朝的故事)
function completeUnitOfWork(unitOfWork: Fiber) {
  let completedWork = unitOfWork;

  do {
    const current = completedWork.alternate;
    const returnFiber = completedWork.return;
    let next = completeWork(current, completedWork);
    if (next !== null) {
      workInProgress = next;
      return;
    }

    const siblingFiber = completedWork.sibling;
    if (siblingFiber !== null) {
      workInProgress = siblingFiber;
      return;
    }

    completedWork = returnFiber as Fiber;
    workInProgress = completedWork;
  } while (completedWork !== null);
}
相关推荐
涛哥码咖14 分钟前
chrome安装AXURE插件后无效
前端·chrome·axure
OEC小胖胖26 分钟前
告别 undefined is not a function:TypeScript 前端开发优势与实践指南
前端·javascript·typescript·web
行云&流水1 小时前
Vue3 Lifecycle Hooks
前端·javascript·vue.js
Sally璐璐1 小时前
零基础学HTML和CSS:网页设计入门
前端·css
老虎06271 小时前
JavaWeb(苍穹外卖)--学习笔记04(前端:HTML,CSS,JavaScript)
前端·javascript·css·笔记·学习·html
三水气象台1 小时前
用户中心Vue3网页开发(1.0版)
javascript·css·vue.js·typescript·前端框架·html·anti-design-vue
灿灿121381 小时前
CSS 文字浮雕效果:巧用 text-shadow 实现 3D 立体文字
前端·css
烛阴2 小时前
Babel 完全上手指南:从零开始解锁现代 JavaScript 开发的超能力!
前端·javascript
AntBlack2 小时前
拖了五个月 ,不当韭菜体验版算是正式发布了
前端·后端·python
31535669132 小时前
一个简单的脚本,让pdf开启夜间模式
前端·后端