一、render阶段
render阶段开始于performSyncWorkOnRoot或performConcurrentWorkOnRoot方法的调用。这取决于本次更新是同步更新还是异步更新
// 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 (执行工作)
-
作用:这是真正干活的地方。它负责:
- beginWork:向下探测子节点,计算 State 更新,生成新的 Fiber 节点。
- completeWork:向上回溯,收集副作用(Effect),构建离屏 DOM 树。
3. 运行过程全解析
-
启动:当有更新(如
setState)发生时,调度器会触发workLoopConcurrent。 -
判断与执行:
- React 检查
workInProgress是否为空。如果不为空,说明还有任务。 - 接着询问调度器: “我现在能继续干活吗?” (调用
shouldYield())。 - 如果调度器说“可以”,React 就执行
performUnitOfWork,处理完当前的 Fiber 节点,并把workInProgress指向下一个节点。
- React 检查
-
中断(Yielding) :
- 如果在执行完某一个 Fiber 节点后,
shouldYield()返回了true(意味着 5ms 的默认时间片到期了,或者有更高优先级的用户输入,如点击事件),while循环就会跳出。 - 关键点:此时任务并没有完成,但 React 会主动把主线程交还给浏览器,让浏览器去渲染 UI、响应点击或处理动画。
- 如果在执行完某一个 Fiber 节点后,
-
恢复(Resuming) :
- 浏览器处理完紧急事务后,调度器会在下一个空闲时间再次调用
workLoopConcurrent。 - 由于
workInProgress变量被保存在内存中,React 能够**“断点续传”**,从上次停下的那个节点继续工作。
- 浏览器处理完紧急事务后,调度器会在下一个空闲时间再次调用
4. 为什么要这么做?(并发模式的核心价值)
在传统的 同步模式(Sync Mode) 中,React 使用的是 workLoopSync:
JavaScript
while (workInProgress !== null) {
performUnitOfWork(workInProgress);
}
由于没有 shouldYield(),一旦 React 开始渲染,它会死死地霸占主线程直到渲染结束。如果组件树很庞大,渲染需要 100ms,那么在这 100ms 内,用户点击按钮、滚动页面,浏览器都不会有任何反应,页面就会显得“卡死”。
workLoopConcurrent 的优势:
- 不阻塞主线程:它把长任务切碎,每执行一小段就停下来看看有没有更紧急的事。
- 高优先级插队:如果在渲染中途用户点击了按钮,React 可以中断当前的低优先级渲染,先处理点击事件,然后再回来继续之前的渲染。
- 流畅的体验:通过时间分片,保证了即使在复杂的计算下,页面依然能保持 60fps 的响应速度。