React 18 并发特性(Concurrent Mode)
一、官方标准定义:React 两大核心阶段
阶段一:「渲染阶段(Reconciliation / 调和阶段)」
也被 React 源码称为 workInProgress(工作中)阶段,这是 React 做「内存计算」的核心阶段。
核心特征
- 纯内存计算,不碰真实DOM:这个阶段只做「虚拟DOM对比(Diff)、构建/更新 Fiber 树、标记DOM的增/删/改操作」,所有操作都在内存中完成,浏览器页面完全没有任何变化,用户完全感知不到这个阶段的存在;
- 无副作用执行:不会执行
useEffect、类组件的componentDidMount/Update生命周期,不会更新 Ref 引用,不会触发任何业务侧的副作用逻辑; - React18 核心变更点:React16/17 中这个阶段是「同步不可中断」的,React18 中这个阶段是「可中断、可恢复、可取消」的(并发特性的唯一作用区域);
- 可丢弃,无任何风险:就算渲染阶段执行到一半被中断/丢弃,也不会有任何页面BUG、内存泄漏,因为没有修改任何真实的浏览器资源,就像「草稿纸上的演算过程被擦掉」一样。
这个阶段到底做了什么?
基于最新的状态,算出「页面需要怎么更新」,并生成一份「DOM更新操作清单」,仅此而已。
阶段二:「提交阶段(Commit)」
这是 React 做「真实浏览器操作」的唯一阶段,也是页面真正发生变化的阶段,React 源码中也叫「完成阶段」。
核心特征
- 操作真实DOM,触发展现变化:唯一会执行「渲染阶段生成的DOM操作清单」的阶段,调用原生 DOM API(
appendChild/removeChild/setAttribute)完成真实DOM的增删改,执行完这一步,用户才能看到页面的变化; - 同步执行、不可中断、原子化完成:一旦提交阶段开始,就必须「一口气执行到底」,JS主线程会被完全占用,任何任务都无法插队、无法中断,哪怕是用户的点击/输入交互也会被暂存。
- 为什么必须不可中断?这是 React 的「保命设计」:DOM是浏览器的共享全局资源,如果DOM更新到一半被中断,会出现页面UI撕裂、DOM节点残缺、状态和视图不一致的致命BUG,这种错误是前端无法修复的,所以「原子化更新」是底线;
- 执行所有副作用逻辑:提交阶段是 React 执行「副作用」的唯一时机,包含:
- 函数组件:执行
useEffect/useLayoutEffect钩子; - 类组件:执行
componentDidMount/componentDidUpdate/componentWillUnmount生命周期; - 所有组件:更新
useRef/createRef的引用指向,让你能拿到真实DOM节点。
- 函数组件:执行
- 耗时极短,几乎无感知阻塞:提交阶段做的都是「执行指令」而非「做计算」,DOM操作是浏览器底层C++实现的,哪怕更新几百个DOM节点,耗时也在 0.1~2ms 之间,远小于浏览器16.67ms的帧刷新间隔,所以我们几乎感知不到交互阻塞。
这个阶段到底做了什么?(一句话说透)
把渲染阶段算出的「DOM更新清单」落地成真实页面变化,并执行所有需要在DOM更新后触发的业务逻辑。
二、先明确核心:React18 的「并发」到底是什么?
2.1 官方定义
React18 的并发特性(Concurrent Rendering) 是一种可中断、可暂停、可恢复、可取消的渲染机制。它允许 React 在渲染一个耗时任务的过程中,临时中断当前渲染,优先去处理优先级更高的任务(比如用户点击/输入/滚动),等高优先级任务执行完毕后,再恢复之前被中断的渲染;如果后续又出现更高优任务,甚至可以直接丢弃之前未完成的渲染任务,避免无效计算。
2.2 核心对比:React18 之前的「同步渲染」
React18 发布前,所有版本的渲染都是同步且不可中断的同步渲染,这个特性是 React 一直以来的性能痛点:
- 工作机制:一旦 React 开始渲染(从
render到commit阶段),就会「一口气执行到底」,在渲染完成前,JavaScript 主线程会被完全占用。 - 致命问题:JS 是单线程语言,主线程被占用时,浏览器的布局(Layout)、绘制(Paint)、用户事件响应都会被阻塞。
- 现象:如果组件层级深、数据量大(比如长列表渲染),页面会出现卡顿、掉帧、用户操作无响应(比如点击按钮没反应、输入框打字延迟),体验极差。
- 本质:同步渲染中「渲染任务」和「用户交互任务」是串行排队的,没有优先级之分。
2.3 一句话总结核心区别
- 同步渲染:渲染一旦开始,必须执行完,阻塞一切,优先级无差别;
- 并发渲染:渲染可以被打断,优先级高的任务插队优先执行,不阻塞用户交互,是「协作式」的渲染机制。
三、为什么 React18 要推出「并发特性」?核心设计目标
React 团队做这个重大重构,核心只有一个目标:在不牺牲代码可读性/开发体验的前提下,解决「渲染阻塞导致的用户体验问题」,让 React 应用的交互更流畅、响应更及时。
补充一个重要细节:
React18 的并发特性不是「多线程渲染」,React 依然运行在浏览器的 JS 主线程中,没有开启新线程!并发的实现是「逻辑层面的调度」,而非「操作系统层面的多线程」,这点一定要记牢,避免理解偏差。
四、并发渲染的 3 个核心关键特性
React 18 的并发能力,核心体现在 3 个不可分割的特性上,这也是理解并发的基石,可中断、可恢复、可取消 三者相辅相成:
特性1:可中断 (Interruptible)
React 在「渲染阶段(Reconciliation)」执行 diff 算法、生成 Fiber 树的过程中,不再是一次性执行,而是会把渲染任务拆分成多个小的执行单元。
- 浏览器有一个「空闲调度机制」,React 会在浏览器的每一帧空闲时间执行一个小单元的渲染任务;
- 一旦浏览器需要执行高优先级任务(用户点击、输入、动画),React 会立刻「中断」当前的渲染小单元,把主线程让出去;
- 核心范围:只有「渲染阶段」可以被中断,「提交阶段(Commit)」(真实DOM更新、执行
useEffect/componentDidMount等)依然是同步不可中断的!因为DOM操作必须原子化,否则会出现页面UI撕裂、状态不一致的问题。
特性2:可恢复 (Resumable)
这是「可中断」的配套能力:当被中断的渲染任务对应的高优先级任务执行完毕后,浏览器主线程再次空闲时,React 不需要「从头开始重新渲染」,而是可以从之前中断的那个执行单元继续往下渲染。
- 实现原理:React18 的 Fiber 架构本身就是「链表结构」,每个 Fiber 节点都记录了当前的渲染进度,这是可恢复的技术基础;
- 核心价值:极大减少无效计算,提升渲染效率,这也是并发渲染比「分片渲染」更优秀的地方。
特性3:可取消 (Cancelable)
这是并发渲染的「灵魂特性」,也是最能体现性能优势的一点,优先级最高的特性:
- 工作机制:如果 React 正在渲染一个低优先级任务(比如列表懒加载、非可视区域的组件渲染),此时触发了一个高优先级任务(比如用户点击按钮、输入框打字、下拉刷新),React 会直接丢弃这个未完成的低优先级渲染任务,不再继续执行,而是立即执行高优先级任务;
- 关键补充:被取消的渲染任务不会留下任何副作用(比如不会更新DOM、不会执行副作用钩子),因为副作用只在「提交阶段」执行,而渲染阶段的计算都是内存中的临时操作;
- 极致体验:用户的交互操作永远不会被「后台的渲染任务」阻塞,用户操作的响应优先级永远是最高的,这彻底解决了 React 应用「点击没反应、打字卡顿」的核心痛点。
五、并发渲染的核心支撑:优先级调度机制
所有的「可中断/可恢复/可取消」,本质上都是基于 React18 内置的 优先级调度系统 实现的,这是并发特性的「底层引擎」,没有优先级调度,并发就是空中楼阁。
5.1 核心原则
React 会为每一个更新任务(setState、useState修改)分配一个优先级,调度器会始终遵循:高优先级任务优先执行,低优先级任务让步于高优先级任务。
5.2 常见任务优先级排序(从高到低,熟记)
React 内置了一套优先级规则,日常开发中能感知到的优先级排序如下:
- 用户交互类(最高):用户输入(input/textarea打字)、鼠标点击、滚动、拖拽、触摸事件 → 这类任务必须立即响应,延迟超过16ms用户就会感知到卡顿;
- 动画过渡类:CSS动画、React过渡动画(比如react-transition-group)、弹窗显隐 → 要求60fps流畅,优先级仅次于用户交互;
- 数据更新类:接口请求成功后的列表数据渲染、表单提交后的反馈渲染 → 优先级中等;
- 后台计算类(最低):懒加载组件、非可视区域的渲染、大数据量的统计计算、防抖后的搜索联想(延迟执行的)→ 这类任务可以被无限中断/取消。
5.3 调度的核心实现:基于浏览器的 requestIdleCallback + 自研调度器
React 没有直接依赖浏览器原生的 requestIdleCallback(因为兼容性和触发频率问题),而是自研了一套更精准的调度器(Scheduler 包),核心逻辑是:
- 利用
requestAnimationFrame监听浏览器的每一帧; - 在每一帧的「空闲时间」执行低优先级的渲染任务;
- 一旦检测到有高优先级任务进入队列,立刻中断当前任务,切换执行高优先级任务。
六、React18 如何开启并发特性?
6.1 关键前提:并发特性是「渐进式启用」的,无破坏性更新
React 团队的核心设计理念:完全向后兼容,升级 React18 后,你的旧项目代码不需要任何修改就能正常运行,因为 React18 的并发特性是「选择启用」的,不是默认开启的。
6.2 开启并发模式的唯一入口:createRoot
React18 发布了两个根节点创建API,这是区分「是否开启并发特性」的唯一标准:
✔️ 旧版 API(同步渲染,默认):ReactDOM.render
import ReactDOM from 'react-dom';
import App from './App';
// React18之前的写法,React18中保留,但运行在「同步渲染模式」
// ❌ 不会启用任何并发特性
ReactDOM.render(<App />, document.getElementById('root'));
✔️ 新版 API(并发渲染,手动开启):ReactDOM.createRoot
import ReactDOM from 'react-dom/client'; // 注意导入路径变化
import App from './App';
// React18的新写法,✅ 完全启用所有并发特性
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);
✅ 核心结论:只有使用 createRoot 创建根节点,你的 React 应用才会运行在「并发模式」下,拥有所有并发特性;使用旧的 ReactDOM.render 则还是原来的同步渲染模式,这也是 React18 「无痛升级」的核心保障。
七、总结:React18 并发特性的核心价值(精华提炼)
- React18 的并发渲染(Concurrent Rendering) 是「可中断、可恢复、可取消」的渲染机制,核心解决「同步渲染阻塞用户交互」的痛点;
- 并发不是多线程,是「逻辑层面的优先级调度」,渲染阶段可中断,提交阶段不可中断;
- 并发特性默认不开启,唯一开启方式是使用
ReactDOM.createRoot替代旧的ReactDOM.render; - 核心配套能力是
useTransition、useDeferredValue、稳定版Suspense,这三个是开发中最常用的并发API; - 并发特性是 React 从「同步渲染」到「异步调度渲染」的里程碑式重构,也是 React 未来所有新特性的基础;
- 核心体验提升:用户交互永远优先,应用再也不会因为渲染而卡顿,这是 React18 最核心的价值。