
我们每天都在写 setState,但很少有人会深入思考 React 在底层究竟执行了哪些操作。只要页面运行流畅,谁会在意这些细节呢?
然而,当你需要渲染一个包含数千行数据的表格,或是构建一个交互复杂的仪表盘时,页面开始掉帧、输入响应变得迟钝的情况就出现了。如果不了解 React 的“车道模型”(Lanes),面对这种性能瓶颈,你可能真的会感到束手无策。
今天,我们就来揭开引擎盖,深入探究一下支撑 React 18 及 19 并发特性的核心调度机制——Lanes 系统是如何工作的。
告别“一根筋”的 Stack 协调时代
时间回溯到 2017 年,React 16 引入了 Fiber 架构,这是一次对协调引擎的彻底重写。
在此之前,React 使用的是“栈式协调器”,其更新过程是同步执行的:一旦开始渲染就必须一次性完成,中途无法被打断。这就像在一条单行道上,如果前面有一辆慢吞吞的拖拉机(一个极其复杂的组件在渲染),那么无论后面是保时捷还是法拉利(用户的点击等高优先级交互事件),都只能乖乖排队等待。
Fiber 架构的核心突破在于实现了增量渲染。它允许 React 暂停当前正在进行的工作,将主线程的控制权让给更紧急的任务(例如响应用户点击),待高优先级任务处理完毕后,再回来继续之前被中断的低优先级渲染。
不过,Fiber 最初采用基于线性“过期时间”的模型来分配任务优先级——等待时间越长的更新,其优先级会被提得越高。这套模型在处理 I/O 密集型更新时表现不佳,因为不同类型的任务需要在独立的“流”中并行处理,而不应互相阻塞。
于是,更精细的 Lanes 模型应运而生。
位运算:React 源码中无处不在的二进制
如果你翻阅过 React 的源码,一定会对其中像 0b00010000 这样的二进制数字感到困惑甚至劝退。实际上,这背后是 React 为了追求极致性能而做出的设计取舍。
Lanes 模型的核心思想是:用一个 32 位整数的每一位(bit),来代表一条独立的“车道”(Lane)。
为什么要采用这种设计?设想一下,如果有 100 个任务需要根据优先级排序,用数组存储可能需要遍历 100 次才能找到最高优先级的任务。但使用二进制位运算:
- 按位或(|)合并更新:
LaneA | LaneB,一次 CPU 指令即可完成。
- 按位与(&)检查过滤:
(Lanes & SyncLane) !== 0,同样只需一次指令。
整个过程的时间复杂度是 O(1),无需循环遍历。对于一个每秒需要执行成千上万次调度决策的系统而言,这种级别的性能压榨是至关重要的。

使用位运算合并更新还有一个额外的好处:它能帮助 React 在不同状态变化间维持 UI 的连贯性。React 可以暂停一个低优先级的渲染任务(例如渲染一个超长的数据列表),立即响应高优先级的 Sync 车道事件(如用户点击)。待紧急任务处理完毕后,再基于相同的车道状态(被合并后的位掩码)恢复之前中断的低优先级工作。这套精妙的并发模式调度机制,是 React 高性能的基石之一。
车道等级制度:谁拥有最高路权?
React 为不同类型的任务设立了严格的优先级等级制度。

SyncLane:拥有绝对路权。用户的离散交互,如点击、按键输入等,都通过这条车道处理。无论 React 当前正在执行什么任务,只要这条车道上有“车辆”(待处理更新),都必须立刻中断手头工作,优先处理它。
InputContinuousLane:次高优先级。用于处理滚动、拖拽等连续交互操作。这类任务允许稍有延迟,但必须保证流畅、不卡顿。
DefaultLane:普通车道。大多数常规的 setState 更新都走这里。
TransitionLanes:慢速/后台车道。这是一个包含 16 条车道的集合,专门为 useTransition 和 startTransition API 服务。这些车道上的任务可以被中断,并支持并行处理。
IdleLane:空闲车道。优先级最低,用于处理屏幕外内容的渲染,或不重要的后台预加载任务。
这套精细的分类体系,使得 React 实现了开发者梦寐以求的目标:高优先级的交互永远保持丝滑流畅,而低优先级的渲染则可以“慢慢来”。
防止“饥饿”:低优先级任务的生存保障
任何优先级调度系统都会面临一个经典问题:饥饿(Starvation)。即低优先级任务因为持续不断的高优先级任务涌入,而永远得不到执行的机会。
React 的解决方案简单而有效:为每条车道设置计时器。
如果一个车道上的更新在 pendingLanes(待处理车道集合)中等待了过长时间,它就会被标记为“已过期”。一旦过期,React 会直接将其优先级提升至 SyncLane,强制执行渲染。这样,即使高优先级任务源源不断,低优先级任务最终也能获得执行机会,确保了 UI 状态的最终一致性。
此外,车道合并还承担着在组件树中传递信号的功能。子组件中待处理的更新会被合并到父组件的 childLanes 字段中,这让 React 能够精确地知道整棵组件树的哪些部分需要被重新处理,从而进行更高效的算法式更新。
实战:useTransition 与 useDeferredValue
了解了底层原理,那么对我们日常开发有什么实际帮助呢?
React 向开发者提供了两个直接与调度器交互的 Hook:useTransition 和 useDeferredValue。你可以把它们看作是手动为 React 调度器选择车道的“换挡杆”。它们都利用 Transition Lanes,将非紧急的更新任务从 SyncLane 中剥离出来,从而避免重型渲染阻塞用户的即时输入。
useTransition:将重型更新导向慢车道
当你能够直接控制状态更新的代码时(例如调用 setSearchTerm),可以使用 useTransition。将更新包裹在 startTransition 回调中,就是在明确告诉 React:“这个更新不紧急,你先处理更要紧的事(比如用户输入)。”
场景:一个企业级仪表盘,用户点击“全局分析”标签页,需要渲染包含数千个数据点的复杂图表。
-
不使用 useTransition 的流程:
- 用户点击“分析”标签。
- React 触发一个紧急更新(SyncLane)。
- 主线程被锁定 500ms 用于计算和渲染图表。
- 用户发现点错了,想立刻返回首页——但不好意思,浏览器完全卡死,必须等待“分析”页渲染完毕。
-
使用 useTransition 的优化:
const [isPending, startTransition] = useTransition();
const [tab, setTab] = useState('home');
function handleTabChange(nextTab) {
// 将这个重型更新“扔进”慢车道(Transition Lane)
startTransition(() => {
setTab(nextTab);
});
}
return (
<nav>
<TabButton onClick={() => handleTabChange('analytics')}>
{isPending ? <Spinner /> : 'Analytics'}
</TabButton>
<TabButton onClick={() => setTab('home')}>Home</TabButton>
</nav>
);
代码中的 isPending 是 React 提供的贴心信号,它告诉你:“后台的重型渲染还在进行中,你可以先展示一个加载状态。”
useDeferredValue:延迟衍生值的艺术
当你无法控制状态更新的源头时(例如该值是通过 props 传递下来的),可以使用 useDeferredValue。它会创建一个值的“延迟镜像”:React 会先使用旧值进行渲染(保证输入等交互的流畅性),然后在后台通过 Transition 车道调度新值的渲染。
场景:搜索框配合一个包含一万条数据的列表进行实时过滤。
function Search() {
const [query, setQuery] = useState("");
const filteredItems = items.filter(item =>
item.name.toLowerCase().includes(query.toLowerCase())
);
return (
<>
<input value={query} onChange={e => setQuery(e.target.value)} />
<List items={filteredItems} />
</>
);
}
function Search() {
const [query, setQuery] = useState("");
const deferredQuery = useDeferredValue(query); // 关键改动
const filteredItems = items.filter(item =>
item.name.toLowerCase().includes(deferredQuery.toLowerCase()) // 使用延迟值
);
return (
<>
<input value={query} onChange={e => setQuery(e.target.value)} />
<List items={filteredItems} />
</>
);
}
核心逻辑在于:query 会立即更新并走 SyncLane,确保输入框响应迅速;而 deferredQuery 则延迟更新并走 Transition 车道,列表的过滤和渲染变为低优先级任务。这样就将一次可能造成卡顿的沉重更新,拆分成了“即时响应”和“后台渐进更新”两个步骤,极大提升了用户体验。
结语:从实验特性到默认范式
曾几何时,并发模式(Concurrent Mode)还是一个需要手动开启的实验性功能。但时至今日,它已成为构建高性能 React 应用的标配。随着 React 19 的成熟,startTransition、useDeferredValue、自动批处理等能力,早已不再是“可选的优化技巧”——它们是我们用来编排应用更新流、保证交互流畅性的核心工具。
理解 Lanes 模型,是深入掌握 React 调度机制的第一步。当然,Lanes 主要解决的是更新优先级的逻辑划分问题。要真正理解 React 是如何在复杂应用中维持 60fps 的流畅度,还需要关注其两侧的配套系统:事件优先级系统(如何将物理交互映射为车道请求)和 调度器(Scheduler)(如何与浏览器主线程协作,按优先级执行任务且不丢帧)。这些话题,我们未来有机会在云栈社区再深入探讨。