react render

0 阅读3分钟

一、render阶段

render阶段开始于performSyncWorkOnRootperformConcurrentWorkOnRoot方法的调用。这取决于本次更新是同步更新还是异步更新

// performSyncWorkOnRoot会调用该方法
function workLoopSync() {
  while (workInProgress !== null) {
    performUnitOfWork(workInProgress);
  }
}

// performConcurrentWorkOnRoot会调用该方法
function workLoopConcurrent() {
  while (workInProgress !== null && !shouldYield()) {
    performUnitOfWork(workInProgress);
  }
}

二、workLoopConcurrent

workLoopConcurrent 是 React 并发模式(Concurrent Mode)的核心引擎。如果说 React 是一台精密运行的机器,那么 workLoopConcurrent 就是这台机器的“心跳”,它决定了任务如何被执行、何时被中断以及如何恢复。

graph TD
    Start([开始 workLoopConcurrent]) --> CheckWIP{workInProgress <br/>是否为空?}
    
    %% 条件判断:是否有任务
    CheckWIP -- 是 --> RenderFinish([进入 Commit 阶段])
    CheckWIP -- 否 --> CheckYield{shouldYield <br/>是否需要让出?}

    %% 条件判断:是否超时间片
    CheckYield -- 是--> Pause[中断并让出主线程]
    Pause --> YieldEnd([等待下一次调度恢复])
    
    CheckYield -- 否 --> PerformUnit[执行 performUnitOfWork]
    
    %% 执行并循环
    PerformUnit --> LoopBack[指向下一个 Fiber 节点]
    LoopBack --> CheckWIP

    style Pause fill:#f96,stroke:#333,stroke-width:2px
    style RenderFinish fill:#bbf,stroke:#333,stroke-width:2px

以下是对 workLoopConcurrent 的详细文字解读:

1. 函数源代码(逻辑简化)

在 React 源码中,它的结构非常简洁,但蕴含的逻辑很深:

JavaScript

function workLoopConcurrent() {
  // 只要还有任务要做,并且【调度器】告诉我们还有剩余时间
  while (workInProgress !== null && !shouldYield()) {
    performUnitOfWork(workInProgress);
  }
}

2. 三个核心要素

A. workInProgress (当前工作单元)

  • 定义:它是一个指针,指向当前正在处理的 Fiber 节点。
  • 作用:React 会把整个 UI 树拆分成一个个微小的“Fiber 节点”。workLoop 的本质就是顺着树结构,一个节点一个节点地向下处理(即 performUnitOfWork)。

B. shouldYield() (时间片检查)

  • 定义:这是从 Scheduler(调度器)引入的一个函数。

  • 作用:它会检查当前这一帧(通常是 16.6ms)是否已经用完。

    • 如果没有用完:返回 false,循环继续。
    • 如果已经用完:返回 true,循环立刻停止,即便任务还没做完。这就是所谓的“时间分片(Time Slicing) ”。

C. performUnitOfWork (执行工作)

  • 作用:这是真正干活的地方。它负责:

    1. beginWork:向下探测子节点,计算 State 更新,生成新的 Fiber 节点。
    2. completeWork:向上回溯,收集副作用(Effect),构建离屏 DOM 树。

3. 运行过程全解析

  1. 启动:当有更新(如 setState)发生时,调度器会触发 workLoopConcurrent

  2. 判断与执行

    • React 检查 workInProgress 是否为空。如果不为空,说明还有任务。
    • 接着询问调度器: “我现在能继续干活吗?” (调用 shouldYield())。
    • 如果调度器说“可以”,React 就执行 performUnitOfWork,处理完当前的 Fiber 节点,并把 workInProgress 指向下一个节点。
  3. 中断(Yielding)

    • 如果在执行完某一个 Fiber 节点后,shouldYield() 返回了 true(意味着 5ms 的默认时间片到期了,或者有更高优先级的用户输入,如点击事件),while 循环就会跳出。
    • 关键点:此时任务并没有完成,但 React 会主动把主线程交还给浏览器,让浏览器去渲染 UI、响应点击或处理动画。
  4. 恢复(Resuming)

    • 浏览器处理完紧急事务后,调度器会在下一个空闲时间再次调用 workLoopConcurrent
    • 由于 workInProgress 变量被保存在内存中,React 能够**“断点续传”**,从上次停下的那个节点继续工作。

4. 为什么要这么做?(并发模式的核心价值)

在传统的 同步模式(Sync Mode) 中,React 使用的是 workLoopSync

JavaScript

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

由于没有 shouldYield(),一旦 React 开始渲染,它会死死地霸占主线程直到渲染结束。如果组件树很庞大,渲染需要 100ms,那么在这 100ms 内,用户点击按钮、滚动页面,浏览器都不会有任何反应,页面就会显得“卡死”。

workLoopConcurrent 的优势:

  • 不阻塞主线程:它把长任务切碎,每执行一小段就停下来看看有没有更紧急的事。
  • 高优先级插队:如果在渲染中途用户点击了按钮,React 可以中断当前的低优先级渲染,先处理点击事件,然后再回来继续之前的渲染。
  • 流畅的体验:通过时间分片,保证了即使在复杂的计算下,页面依然能保持 60fps 的响应速度。