两者分别对应同步渲染 和并发渲染,是 React 调和(Reconciler)阶段的总控函数
只有
TransitionLane/RetryLane/IdleLane走并发路径
| 函数 | 设计思想 | 核心目标 | 调度模式 |
|---|---|---|---|
| renderRootSync | 原子性渲染 | 保证渲染立即完成,不允许中断 | 同步执行(无时间切片) |
| renderRootConcurrent | 并发协作式渲染 | 保证UI 不卡顿,渲染让步于用户交互 / 浏览器重绘 | 时间切片(Time Slicing)+ 优先级调度 |
一、完整调用链
renderRootSync 流程图
scss
renderRootSync(root, lanes, shouldYieldForPrerendering)
│
├─ executionContext |= RenderContext
├─ prepareFreshStack(root, lanes)
│
└─ do-while 循环 (重试循环)
│ │
│ ├─ try
│ │ ├─ workInProgressSuspendedReason ≠ NotSuspended?
│ │ │ ├─ SuspendedOnHydration → break outer (放弃)
│ │ │ ├─ SuspendedOnData/Action/Immediate → throwAndUnwindWorkLoop
│ │ │ │ → 立即渲染 fallback
│ │ │ └─ default → throwAndUnwindWorkLoop
│ │ │
│ │ ├─ workLoopSync()
│ │ │ └─ while (workInProgress !== null)
│ │ │ └─ performUnitOfWork(workInProgress)
│ │ │ ├─ beginWork(current, unitOfWork, renderLanes)
│ │ │ └─ completeUnitOfWork(unitOfWork)
│ │ │
│ │ └─ break (正常退出循环)
│ │
│ └─ catch (thrownValue)
│ └─ handleThrow(root, thrownValue) ← 设置挂起原因,重试
│
├─ root.shellSuspendCounter++ (若 didSuspendInShell)
├─ resetContextDependencies()
├─ executionContext 恢复
│
├─ workInProgress !== null? → 不清理(Shell 挂起)
└─ workInProgress === null? → 清理 workInProgressRoot
│ → finishQueueingConcurrentUpdates()
│
└─ return exitStatus
renderRootConcurrent 流程图
kotlin
renderRootConcurrent(root, lanes)
│
├─ executionContext |= RenderContext
├─ prepareFreshStack(root, lanes) 或 复用已有栈
│
└─ do-while 循环 (重试循环)
│ │
│ ├─ try
│ │ ├─ workInProgressSuspendedReason ≠ NotSuspended?
│ │ │ ├─ SuspendedOnError → throwAndUnwindWorkLoop
│ │ │ ├─ SuspendedOnData/Action:
│ │ │ │ ├─ 数据已到 → replaySuspendedUnitOfWork (重放)
│ │ │ │ └─ 数据未到 → thenable.then(ping) → break outer (中断)
│ │ │ ├─ SuspendedOnImmediate → 标记 ready → break outer
│ │ │ ├─ SuspendedOnInstance → 标记 ready → break outer
│ │ │ ├─ SuspendedAndReadyToContinue → 重放 / unwind
│ │ │ ├─ SuspendedOnInstanceAndReadyToContinue → 跳过 / unwind
│ │ │ └─ SuspendedOnHydration → break outer
│ │ │
│ │ ├─ workLoopConcurrent() / workLoopConcurrentByScheduler()
│ │ │ └─ while (workInProgress !== null && !shouldYield())
│ │ │ └─ performUnitOfWork(workInProgress)
│ │ │
│ │ └─ break (正常退出循环,或中断)
│ │
│ └─ catch (thrownValue)
│ └─ handleThrow(root, thrownValue)
│
├─ resetContextDependencies()
├─ executionContext 恢复
│
├─ workInProgress !== null?
│ └─ return RootInProgress ← 关键:标记中断
│
└─ workInProgress === null?
├─ workInProgressRoot = null
├─ finishQueueingConcurrentUpdates()
└─ return workInProgressRootExitStatus
二、代码解析
步骤一、前置环境准备 - 上下文标记
javascript
const prevExecutionContext = executionContext;
executionContext |= RenderContext; // 设置 RenderContext 位 (0b010)
const prevDispatcher = pushDispatcher(root.containerInfo);
const prevAsyncDispatcher = pushAsyncDispatcher();
作用:
- 记录旧的
executionContext,函数结束时恢复 - 标记当前为渲染阶段 → 阻止
flushSync等在渲染中执行 - 注入事件分发器(Hooks dispatching)和异步分发器
设计意义 :executionContext 在整个渲染过程中用于判断"我们在做什么"------渲染阶段中任何外部尝试 setState 会走 render 阶段内更新路径
步骤二、栈准备 - Root 准备与重置
javascript
if (workInProgressRoot !== root || workInProgressRootRenderLanes !== lanes) {
// 获取 Transition 信息,标记本次渲染属于哪个 Transition
workInProgressTransitions = getTransitionsForLanes(root, lanes);
// → 创建全新 WiP 树,准备全新的渲染栈 → 重置所有状态
prepareFreshStack(root, lanes); (
}
// renderRootConcurrent 独有:
// 触发条件:并发渲染时间片用完,被高优先级更新插队中断,之后重新恢复执行
else {
// 检查:是否是预渲染
workInProgressRootIsPrerendering = checkIfRootIsPrerendering(root, lanes);
}
作用:
- 若 root 或 lanes 变了 → 重置整个渲染栈,重置所有渲染状态,创建新 WorkInProgress 树,初始化
workInProgressRoot= 当前应用根节点(prepareFreshStack) - 获取 Transition 信息(
useTransition/useDeferredValue支持):根据优先级(lanes)找到对应的 Transition 任务,标记本次渲染属于哪个 Transition(getTransitionsForLanes) - 若没变(并发中断后恢复场景)→ 复用现有栈,只检查是否是预渲染(
checkIfRootIsPrerendering),继续执行上次中断的位置
设计意义:
- 双缓冲:
current不变,workInProgress可变。prepareFreshStack通过createWorkInProgress克隆 current 树 - 性能最大化,中断恢复不需要从头计算
- 保证每次渲染都是干净的上下文,避免历史状态污染
步骤三、核心循环结构
renderRootSync
javascript
outer: do {
try {
if (
workInProgressSuspendedReason !== NotSuspended &&
workInProgress !== null
) {
// 同步渲染下,我们绝不 yield 主线程,立即回退栈!可能会回退到fallback或错误边界
const unitOfWork = workInProgress;
const thrownValue = workInProgressThrownValue;
switch (workInProgressSuspendedReason) {
case SuspendedOnHydration: {
resetWorkInProgressStack();
exitStatus = RootSuspendedAtTheShell;
break outer; // ← 直接放弃整次渲染
}
case SuspendedOnImmediate:
case SuspendedOnData:
case SuspendedOnAction:
case SuspendedOnDeprecatedThrowPromise: {
if (getSuspenseHandler() === null) {
didSuspendInShell = true;
}
const reason = workInProgressSuspendedReason;
// 1. 清空状态
workInProgressSuspendedReason = NotSuspended;
workInProgressThrownValue = null;
// 2. 【核心】立即回退栈!不等待 Promise!
throwAndUnwindWorkLoop(root, unitOfWork, thrownValue, reason);
// 3. 预渲染特殊处理(退出)
if (
shouldYieldForPrerendering &&
workInProgressRootIsPrerendering
) {
exitStatus = RootInProgress; // ← 预渲染特殊处理
break outer;
}
break; // ← 继续循环
}
default: {
const reason = workInProgressSuspendedReason;
workInProgressSuspendedReason = NotSuspended;
workInProgressThrownValue = null;
throwAndUnwindWorkLoop(root, unitOfWork, thrownValue, reason);
break; // ← 继续循环
}
}
}
workLoopSync(); // 继续执行(unwind 后 workInProgress 可能不为 null)
exitStatus = workInProgressRootExitStatus;
break;
} catch (thrownValue) {
// 设置挂起状态,让下一轮循环处理
handleThrow(root, thrownValue);
}
} while (true);
策略总结:
| 挂起原因 | 同步处理 |
|---|---|
SuspendedOnHydration- 注水挂起 |
resetWorkInProgressStack 放弃整棵树 → 切到水合优先级 |
SuspendedOnImmediate/Data/Action/DeprecatedThrowPromise- 其他所有挂起 |
立即 unwind → 不等待数据,立即向上找 Suspense 边界,显示 Fallback |
设计思想 :同步渲染中不等待任何异步数据 - 直接展开上游 Suspense 边界的 fallback。用户看到的是"立即的 fallback UI",而不是等待
renderRootConcurrent
javascript
outer: do {
try {
if (
workInProgressSuspendedReason !== NotSuspended &&
workInProgress !== null
) {
// 并发渲染下,要么回退,要么重试(灵活处理)
const unitOfWork = workInProgress;
const thrownValue = workInProgressThrownValue;
resumeOrUnwind: switch (workInProgressSuspendedReason) {
case SuspendedOnError: {
workInProgressSuspendedReason = NotSuspended;
workInProgressThrownValue = null;
throwAndUnwindWorkLoop(
root,
unitOfWork,
thrownValue,
SuspendedOnError,
);
break; // ← unwind 后继续循环
}
case SuspendedOnData:
case SuspendedOnAction: {
const thenable = thrownValue;
// ← 数据已到 → 重试
if (isThenableResolved(thenable)) {
workInProgressSuspendedReason = NotSuspended;
workInProgressThrownValue = null;
replaySuspendedUnitOfWork(unitOfWork);
break;
}
const onResolution = () => {
if (
(workInProgressSuspendedReason === SuspendedOnData ||
workInProgressSuspendedReason === SuspendedOnAction) &&
workInProgressRoot === root
) {
workInProgressSuspendedReason = SuspendedAndReadyToContinue;
}
ensureRootIsScheduled(root);
};
// 数据未到 → 加监听,暂停渲染
thenable.then(onResolution, onResolution);
break outer; // ← 退出循环,等待唤醒
}
case SuspendedOnImmediate: {
// 标记为可恢复
workInProgressSuspendedReason = SuspendedAndReadyToContinue;
break outer; // ← 给主线程一个暂停
}
case SuspendedOnInstance: {
workInProgressSuspendedReason =
SuspendedOnInstanceAndReadyToContinue;
break outer; // ← 等资源加载
}
case SuspendedAndReadyToContinue: {
const thenable: Thenable<mixed> = (thrownValue: any);
if (isThenableResolved(thenable)) {
workInProgressSuspendedReason = NotSuspended;
workInProgressThrownValue = null;
replaySuspendedUnitOfWork(unitOfWork); // ← 继续
} else {
workInProgressSuspendedReason = NotSuspended;
workInProgressThrownValue = null;
throwAndUnwindWorkLoop(
root,
unitOfWork,
thrownValue,
SuspendedAndReadyToContinue,
); // ← 数据已错过 → unwind
}
break;
}
case SuspendedOnInstanceAndReadyToContinue: {
let resource: null | Resource = null;
switch (workInProgress.tag) {
case HostHoistable: {
resource = workInProgress.memoizedState;
}
case HostComponent:
case HostSingleton: {
const hostFiber = workInProgress;
const type = hostFiber.type;
const props = hostFiber.pendingProps;
const isReady = resource
? preloadResource(resource)
: preloadInstance(hostFiber.stateNode, type, props);
if (isReady) {
workInProgressSuspendedReason = NotSuspended;
workInProgressThrownValue = null;
const sibling = hostFiber.sibling; // ← 跳过此 fiber
if (sibling !== null) {
workInProgress = sibling;
} else {
const returnFiber = hostFiber.return;
if (returnFiber !== null) {
workInProgress = returnFiber;
completeUnitOfWork(returnFiber);
} else {
workInProgress = null;
}
}
break resumeOrUnwind;
}
break;
}
default: {
break;
}
}
workInProgressSuspendedReason = NotSuspended;
workInProgressThrownValue = null;
throwAndUnwindWorkLoop(
root,
unitOfWork,
thrownValue,
SuspendedOnInstanceAndReadyToContinue,
); // ← 继续等待
break;
}
case SuspendedOnDeprecatedThrowPromise: {
workInProgressSuspendedReason = NotSuspended;
workInProgressThrownValue = null;
throwAndUnwindWorkLoop(
root,
unitOfWork,
thrownValue,
SuspendedOnDeprecatedThrowPromise,
);
break;
}
case SuspendedOnHydration: {
resetWorkInProgressStack();
workInProgressRootExitStatus = RootSuspendedAtTheShell;
break outer; // ← 同同步
}
default: {
throw new Error(
'Unexpected SuspendedReason. This is a bug in React.',
);
}
}
}
if (enableThrottledScheduling) {
workLoopConcurrent(includesNonIdleWork(lanes));
} else {
workLoopConcurrentByScheduler();
}
break;
} catch (thrownValue) {
// 设置挂起状态,让下一轮循环处理
handleThrow(root, thrownValue);
}
} while (true);
策略总结:
| 挂起原因 | 并发处理 | 与同步的差异 |
|---|---|---|
SuspendedOnError - 渲染抛出错误 |
清空挂起状态,回退 Fiber 栈(从出错节点向上回溯,找到 ErrorBoundary),让错误边界捕获错误 | 相同 |
SuspendedOnData/Action - 等待异步数据 |
数据已到 → replaySuspendedUnitOfWork 重新渲染该节点;未到 → 监听完成事件,暂停渲染,让出主线程,直接跳出循环,下次恢复 |
同步立即 throwAndUnwindWorkLoop 回退栈 |
SuspendedOnImmediate - 立即暂停 |
标记 SuspendedAndReadyToContinue 可恢复,短暂暂停,让出主线程,直接跳出循环,下次恢复 |
同步立即 throwAndUnwindWorkLoop 回退栈 |
SuspendedOnInstance - 宿主实例加载(图片 / 样式 / 脚本等) |
标记 SuspendedOnInstanceAndReadyToContinue 等待,暂停渲染,等待资源就绪,让出主线程,直接跳出循环,下次恢复 |
同步立即 throwAndUnwindWorkLoop 回退栈 |
SuspendedAndReadyToContinue - 已就绪,可以恢复 |
数据就绪 → 重置状态,replaySuspendedUnitOfWork 重新渲染该节点 ;未就绪 → throwAndUnwindWorkLoop 回退栈 |
不存在此状态(同步无 recall) |
SuspendedOnInstanceAndReadyToContinue - 宿主实例已就绪 |
检查资源是否加载完成。就绪 → 继续渲染下一个兄弟/父节点;未就绪 → throwAndUnwindWorkLoop 回退栈 |
不存在此状态(同步无 recall) |
SuspendedOnHydration |
resetWorkInProgressStack 中断当前渲染,切换到水合优先级 |
相同 |
SuspendedOnDeprecatedThrowPromise - 旧版 Throw Promise |
throwAndUnwindWorkLoop 回退栈,不重试 |
相同 |
设计思想 :所有中断都能 "暂停 - 保存 - 恢复":不销毁 workInProgress,不丢失渲染进度,不重复执行已完成工作
核心工作流程
- 处理挂起逻辑 (核心差异区)
- 执行工作循环
- 设置退出状态
差异
| 维度 | Concurrent 版 |
Sync 版 |
|---|---|---|
| Suspense 处理 | 等待 Promise 完成,暂停渲染 |
立即回退栈 ,直接显示 Fallback / 错误 |
| 工作循环 | workLoopConcurrent (带时间切片) |
workLoopSync (死循环到底) |
workworkLoopSync VS workLoopConcurrent
javascript
function workLoopSync() {
while (workInProgress !== null) {
performUnitOfWork(workInProgress);
}
}
function workLoopConcurrent(nonIdle: boolean) {
if (workInProgress !== null) {
// 时间分片
const yieldAfter = now() + (nonIdle ? 25 : 5);
do {
performUnitOfWork(workInProgress);
} while (workInProgress !== null && now() < yieldAfter);
}
}
设计思想
do-while确保发生 throw 后重新尝试(handleThrow设置挂起原因后继续)break outer跳出外层循环(挂起/中断时)handleThrow是 React 的异常捕获机制:组件throw的 Promise 被拦截,设置workInProgressSuspendedReason后重新进入循环
步骤四、上下文恢复
javascript
// 清空临时变量
resetContextDependencies();
// 顺序不同
// renderRootSync 独有:
// 重置 executionContext
executionContext = prevExecutionContext;
popDispatcher(prevDispatcher);
popAsyncDispatcher(prevAsyncDispatcher);
// renderRootConcurrent 独有:
popDispatcher(prevDispatcher);
popAsyncDispatcher(prevAsyncDispatcher);
executionContext = prevExecutionContext;
作用
- 修改全局标记 :标记当前不再处于渲染阶段
resetContextDependencies:清空本次渲染中收集的Context依赖,重置React内部的dependencies缓存popDispatcher(prevDispatcher):恢复Hooks调度器 ,退出渲染环境,回到普通非渲染环境,Hooks依赖它判断是否能调用popAsyncDispatcher(prevAsyncDispatcher):恢复异步调度器 (用于Suspense、Transition、异步渲染)
设计思想
- 同步模式:先恢复上下文 → 再弹出 dispatcher 。同步渲染 不允许中断 / 暂停 ,必须先退出渲染上下文,立即关闭 "渲染模式",清理期间不希望触发任何新调度、暂停、恢复,所以必须先把全局状态恢复成 "非渲染",再清理 Hooks,用来确保清理过程绝对安全、不被打断
- 并发模式:先弹出 dispatcher → 最后恢复上下文 。并发渲染 允许中断、恢复、重入 ,而且并发渲染在清理过程中,可能被新的高优先级更新 "插队" !并发模式必须保持渲染上下文直到完全清理完毕 ,保证只要 dispatcher 还没恢复完成,就依然处于渲染上下文保护中,防止在 "半清理状态" 下被新渲染重入,造成崩溃
步骤五、结尾清理和返回
javascript
// 1. 核心判断:内存中的 Fiber 渲染树是否还有未完成的节点
if (workInProgress !== null) {
// 树未完成(原因:Suspense 挂起 / 并发时间片用完 / 高优先级插队)
// renderRootSync
// renderRootConcurrent: 返回 RootInProgress
return RootInProgress; // ← 关键:返回 RootInProgress 通知调用者不要 commit,等下次调度
} else {
// 2. 树已完成:所有节点调和完毕
// 清空根节点标记
workInProgressRoot = null;
// 清空优先级
workInProgressRootRenderLanes = NoLanes;
// 把渲染过程中"并发产生的新更新"加入队列
finishQueueingConcurrentUpdates();
}
return exitStatus;
作用
- 树未完成 → 不清理 workInProgressRoot,保留
workInProgress断点,方便下次恢复直接从中断处继续,不重头开始,不提交 DOM (commit 阶段不会执行)。对于Concurrent时间片用完、被高优插队都会走到这里,对于Sync只有遇到Suspense才会走到这里 - 树已完成 → 清空内存根节点,表示本次渲染彻底结束,清空渲染优先级,把渲染过程中新产生的并发更新正式入队,让新更新不会丢失,下一轮会执行
设计意义:
- 不清理 workInProgress → 实现中断恢复,不重复渲染已完成节点,不丢失进度,不浪费计算
- 严格分离渲染阶段 和提交阶段 :渲染没完成,禁止提交
DOM,DOM永远只在渲染完全完成后才更新,不会出现中间状态、UI 错乱、部分更新 - 并发更新的安全入队:
finishQueueingConcurrentUpdates在渲染完成后处理更新------渲染期间concurrentQueues[]中堆积的交插更新,此时才真正链入 sharedQueue。
三、设计思想
- 同步渲染不等待 :
renderRootSync中任何 Suspend 都立即 unwind → fallback - 并发渲染可中断 :
renderRootConcurrent中每 fiber 检查时间片 - 异常捕获重试 :
do-while+handleThrow:组件 throw 后设置挂起原因,重试循环 - 重放 vs 回退 :并发中数据已到达 → 重放(
replaySuspendedUnitOfWork),节省工作量 - 渲染完成后再入队 :
finishQueueingConcurrentUpdates在完成后统一处理