一、什么是双缓冲模式:
现实生活中的例子,你去看话剧,需要无间断专场,就需要准备两个舞台场景,例如室内切换到到室外;
工作人员需要准备两个场景,室内场景表演完成后熄灯,演员直接走入第二个 室外场景;
对应react中则是:
current树 与 workInProgress树 可以对标 “双缓冲” 模式下的两套缓冲数据
当 current树呈现在用户眼前时,所有的更新都会由 workInProgress 树来承接
workInProgress树将会在用户看不到的地方(内存里)悄悄地完成所有改变
双缓冲如何工作?
1. 初始化
- 应用首次渲染时,只有一棵
current树。 - 当发生更新(如 setState),React 会创建一棵新的
workInProgress树。
2. 构建新树
- React 在
workInProgress树上逐步计算新的 Fiber 节点(可中断、可恢复)。 - 这个过程不会影响当前显示的
current树,用户界面保持稳定。
3. 提交(Commit)
-
当
workInProgress树构建完成且无错误,React 会原子性地将workInProgress树切换为current树。 -
同时,原来的
current树被回收,作为下一次更新的workInProgress树的“缓冲池”(复用节点,减少内存分配)。🔄 交替复用:两棵树的角色在每次更新后互换,形成循环利用。
总结:
- React 双缓冲 = 两棵 Fiber 树(current + workInProgress)交替使用;
- 目的是实现 安全、高效、可中断的异步渲染;
- 是 Fiber 架构和并发特性的基石之一;
- 对开发者透明,但理解它有助于深入掌握 React 性能优化原理
二、时间切片
同步渲染模式下的render 阶段是一个同步的、深度优先搜索的过程
同步状态的调用栈是这样的:
图中有一个很长的task任务,这就是同步带来的问题,它可能会阻塞浏览器,出现卡顿;
当使用异步并发时:
现在变为多个 task,一个大概在5ms左右;全部加起来和上面时间是差不多的;
但是中间的时间间隙留给了浏览器空闲选择,是否让出主线程;
切片时长是由:react根据浏览器的帧率 动态计算出来的,与浏览器的性能有关;
时间切片的核心思想
利用浏览器每一帧的空闲时间,分批处理 React 更新工作
浏览器一帧(通常 16.7ms)的工作流程大致如下:
[ 处理用户输入 ] → [ 执行 JS ] → [ 样式计算 ] → [ 布局 ] → [ 绘制 ] → [ 合成 ]
React 的时间切片会在 JS 执行阶段 中:
- 检查当前帧(每帧 16.7ms左右)还剩多少时间;
- 如果有足够时间(比如 >5ms),就处理一小部分 Fiber 节点;
- 如果时间快用完,就暂停工作,把控制权交还给浏览器;
- 下一帧继续处理剩余工作。
这个过程由 Scheduler(调度器) 控制,基于 MessageChannel 或 postMessage 实现微任务调度(比 setTimeout 更精准)。
总结:
- 时间切片 = 把大任务拆小 + 在浏览器空闲时执行;
- 它是 React 并发渲染的基础能力之一;
- 依赖 Fiber 架构 + Scheduler 调度器 实现;
- 开发者通过
startTransition/useDeferredValue可主动优化性能; - 需使用
createRoot启用并发模式才能生效。
三、优先级调度
React 的优先级调度(Priority-based Scheduling) 是 React 16 引入 Fiber 架构后的一项核心机制,用于优化 UI 渲染性能,确保高优先级任务(如用户交互)能够优先执行,而低优先级任务(如后台数据更新)可以被延迟或中断,从而提升用户体验。
React 将不同来源的更新赋予不同的优先级。以下是常见的优先级类型(从高到低):
| 优先级类型 | 触发场景 | 说明 |
|---|---|---|
| Immediate(立即) | ReactDOM.flushSync、错误边界恢复 | 最高优先级,同步执行 |
| User-blocking(用户阻塞) | 用户输入(如点击、按键) | 需要快速响应,避免卡顿 |
| Normal(普通) | setTimeout、Promise.then、网络请求回调 | 默认优先级 |
| Low(低) | useDeferredValue、startTransition 中的更新 | 可以稍后处理 |
| Disable (Idle)(空闲) | requestIdleCallback 类似行为 | 只在浏览器空闲时执行 |
注意:React 18 后,这些优先级被进一步抽象为 Lane 模型(车道模型),用位运算高效管理多个并发更新的优先级组合。
总结:
- React 优先级调度的核心目标:让 UI 响应更快、更流畅。
- 通过 Fiber + Lane 模型 + Scheduler 实现任务的中断、恢复、重排。
- 开发者可通过
startTransition、useDeferredValue等 API 主动控制更新优先级。 - 这是 React 实现“并发模式(Concurrent Mode) ”的基础能力之一。
使用:
- startTransition
- useDeferredValue
- suspense
注意:时间切片只对并发模式(Concurrent Mode)下的更新生效。如果你使用 ReactDOM.render(legacy 模式),则不会启用时间切片;需使用 createRoot。
四、渲染 Render 过程
1. 整体流程概览
React 渲染分为两个主要阶段:
Render 阶段(可中断、异步)
- 目标:构建新的 Fiber 树(WorkInProgress Tree)
- 特点:可暂停、可恢复、可丢弃
- 涉及机制:Fiber、时间切片、调度器、优先级调度
Commit 阶段(不可中断、同步)
- 目标:将新 Fiber 树应用到 DOM
- 特点:必须连续执行,不能被打断
- 涉及机制:副作用(useEffect、ref 等)的执行、DOM 更新
✅ 关键思想:把“计算”和“提交”分离,让“计算”可以被中断,而“提交”保持原子性。
2.流程详解
步骤 1:触发更新(如 setState、useState)
setCount(c => c + 1);
- React 将此次更新封装为一个 Update 对象;
- 根据触发源(用户点击、setTimeout、Transition 等),赋予不同 优先级(Lane) ;
- 将更新加入 Fiber 节点的
updateQueue。
步骤 2:调度器(Scheduler)介入
-
React 调用
Scheduler.unstable_scheduleCallback(priority, work); -
调度器根据优先级决定何时执行渲染任务:
- 高优先级(如点击)→ 立即安排;
- 低优先级(如 Transition)→ 等待浏览器空闲。
💡 调度器使用
MessageChannel实现微任务队列,比setTimeout(0)更精准控制帧时机。
步骤 3:进入 Render 阶段(Fiber + 时间切片)
a) 初始化 WorkInProgress 树
- React 创建或复用一棵 WorkInProgress Fiber 树(双缓冲机制);
- 每个节点通过
fiber.alternate指向 current 树的对应节点,实现内存复用。
b) 遍历组件树(BeginWork → CompleteWork)
-
从根节点开始,深度优先遍历(DFS);
-
对每个 Fiber 节点:
- 调用函数组件或类组件的 render;
- 协调(Reconcile)子节点,生成新的子 Fiber;
- 标记副作用(如插入、更新、删除)。
c) 时间切片介入
- 每处理几个 Fiber 节点,检查当前帧剩余时间(deadline);
- 如果时间快用完(如 >5ms 已用),暂停工作,交还主线程给浏览器;
- 下一帧继续从断点恢复(Fiber 天然支持暂停/恢复)。
✅ 这就是 时间切片:把大任务切成小块,在多个帧中完成。
d) 并发能力体现
-
如果在 Render 阶段有更高优先级更新进来(如用户又点了按钮):
- React 丢弃当前 WorkInProgress 树;
- 重新基于最新状态构建更高优的树;
- 低优先级更新可能被“跳过”或“重做”。
这就是 并发渲染:多个更新可以“竞争”,高优胜出。
步骤 4:Render 阶段完成 → 进入 Commit 阶段
- 当整棵 WorkInProgress 树构建完成且无错误;
- React 原子性地将
root.current = workInProgress(双缓冲切换); - 开始 Commit 阶段(同步、不可中断)。
Commit 分三小步:
-
Before Mutation
- 执行
getSnapshotBeforeUpdate; - 读取 DOM 状态(如滚动位置)。
- 执行
-
Mutation
- 批量执行 DOM 操作(插入、更新、删除);
- 这是实际修改 UI 的时刻。
-
Layout
- 执行
useLayoutEffect和componentDidMount/Update; - 可以同步读写 DOM(但要小心性能)。
- 执行
⚠️ Commit 必须同步,因为涉及 DOM 操作,不能中途被打断。
步骤 5:Post-Commit(异步副作用)
- 执行
useEffect(通过Scheduler异步调度); - 不阻塞浏览器绘制,避免卡顿。
3. 关键机制协同关系图
[ setState / useState ]
↓
[ 调度器 Scheduler ] ← 根据优先级安排任务
↓
[ Render 阶段 ]
├─ Fiber 架构:可中断的树遍历
├─ 双缓冲:current ↔ workInProgress
├─ 时间切片:分帧执行,不阻塞主线程
└─ 并发:高优更新可打断低优
↓
[ Commit 阶段 ] ← 同步、原子性更新 DOM
↓
[ useEffect 异步执行 ]
4. 开发者如何配合?
| 场景 | 推荐 API | 作用 |
|---|---|---|
| 用户输入响应 | 默认行为 | 高优先级,立即渲染 |
| 搜索过滤大量数据 | startTransition | 降级为低优先级,启用时间切片 |
| 延迟显示非关键内容 | useDeferredValue | 避免阻塞主 UI |
| 启用并发能力 | createRoot | 必须使用新 Root API |
5. 总结
React 的现代渲染是一个智能调度系统:
- Fiber 提供可中断的单元结构;
- 双缓冲 保证更新安全与内存复用;
- 调度器 + 优先级 决定“谁先干”;
- 时间切片 决定“怎么干”(分帧);
- 并发模式 允许“边干边换人”;
- Commit 阶段 确保最终更新原子生效。
💡 最终目标:让高优先级交互始终流畅,低优先级工作默默完成。