面试题:React 中 Lane 和 Scheduler 是怎么配合的?

一、核心思路(一句话)

Lane 用来表示更新的优先级,Scheduler 负责按照优先级安排任务什么时候执行;Render 过程中如果发现更高优先级的工作,就可以暂停当前 Render,先处理更重要的更新。


二、Lane 的优先级有哪些?

核心思路(一句话)

Lane 本质上是一组用位表示的更新优先级,React 根据不同 Lane 判断哪些更新应该优先处理。

常见的几类可以这样理解:

Lane 常见场景 优先级
SyncLane flushSync 等需要立即处理的更新 最高
InputDiscreteLane 点击、键盘输入等离散交互 高
InputContinuousLane 滚动、拖拽等连续交互 较高
DefaultLane 普通 setState 普通
TransitionLanes startTransition 中的更新 较低
IdleLane 非紧急、空闲时处理的工作 很低

例如:

js 复制代码
// 普通更新
setCount(count + 1);

// Transition 更新
startTransition(() => {
  setSearchResult(result);
});

可以简单理解成:

text 复制代码
点击按钮
  ↓
高优先级更新
  ↓
优先处理

startTransition
  ↓
Transition 更新
  ↓
可以晚一点处理

这里真正要记住的不是 Lane 名字

真正重要的是:

Lane 给每个更新打上优先级标签,React 根据这个标签决定哪些工作应该先处理。

而且 Lane 不只是简单的"优先级数字"。它还承担把更新进行分组的作用,React 可以通过多个 Lane 的组合判断当前这一轮应该处理哪些更新。


三、Scheduler 是干什么的?

核心思路(一句话)

Scheduler 就是负责安排任务什么时候执行,并根据优先级决定先执行哪个任务。

可以把它和 Lane 分开理解:

text 复制代码
Lane
 ↓
这个更新有多重要?
 ↓
Scheduler
 ↓
什么时候执行这个任务?
 ↓
React Work Loop
 ↓
具体处理哪些 Fiber?

所以:

Lane 解决"谁更重要",Scheduler 解决"什么时候做"。


四、Scheduler 底层是怎么工作的?

核心思路(一句话)

Scheduler 会维护待执行的任务,根据优先级选择任务执行,并通过时间切片让任务执行一段时间后主动让出主线程。

整体过程:

text 复制代码
React 收到更新
      ↓
给更新分配 Lane
      ↓
根据优先级安排 Scheduler 任务
      ↓
Scheduler 维护任务队列
      ↓
选择应该执行的任务
      ↓
进入 React Render
      ↓
一个个处理 Fiber
      ↓
检查是否应该让出
      ↓
   ┌──┴──┐
   ↓     ↓
  否      是
   ↓      ↓
继续    停止当前工作
          ↓
       安排下一次任务
          ↓
       当前 Task 结束
          ↓
       浏览器获得机会
          ↓
       下一次 Scheduler 任务
          ↓
       继续处理剩余 Fiber

五、Scheduler 是怎么"让出主线程"的?

这是面试最容易讲错的地方。

核心思路(一句话)

Scheduler 不是把 React 扔到另一个线程,而是让当前 JavaScript Task 主动结束,然后把下一段工作安排到后续 Task 执行。

例如 React 正在处理:

text 复制代码
Fiber A
Fiber B
Fiber C
Fiber D
Fiber E

执行到 C:

text 复制代码
A ✓
B ✓
C ✓
D ← 下一步
E

React 判断:

js 复制代码
shouldYield()

发现应该让出,于是停止当前工作循环:

text 复制代码
Fiber C
  ↓
shouldYield()
  ↓
停止继续处理
  ↓
当前 React 工作函数返回
  ↓
当前 JavaScript Task 结束

这才是真正的"让出"。 不是浏览器突然把 React 从中间掐掉。


六、MessageChannel 在这里做什么?

核心思路(一句话)

MessageChannel 用来安排下一次 React 工作,让当前 Task 结束后,React 再回来继续处理。

简化代码:

js 复制代码
const channel = new MessageChannel();

channel.port1.onmessage = () => {
  workLoop();
};

function scheduleNext() {
  channel.port2.postMessage(null);
}

React 需要让出时:

js 复制代码
if (shouldYield()) {
  scheduleNext();
  return;
}

于是:

text 复制代码
当前 Task
────────────────────
React Render
Fiber A
Fiber B
Fiber C
shouldYield()
    ↓
postMessage()
    ↓
workLoop 返回
────────────────────
当前 Task 结束


后续 Task
────────────────────
MessageChannel
    ↓
onmessage()
    ↓
workLoop()
    ↓
Fiber D
Fiber E
Fiber F
────────────────────

所以要特别注意:

MessageChannel 本身不会中断 JavaScript。

它做的是:

安排一个后续的 MessageChannel Task。

真正的"暂停"是 React 自己:

text 复制代码
shouldYield()
 ↓
停止工作循环
 ↓
当前 JS Task 结束

七、为什么 MessageChannel 能做到这一点?

因为:

js 复制代码
channel.port2.postMessage(null);

是异步的。它不会立即调用:

js 复制代码
channel.port1.onmessage();

而是向浏览器安排一个后续的消息任务。

所以:

text 复制代码
Task0
│
├── React Render
├── postMessage()
└── Task0结束
       ↓
浏览器继续调度任务
       ↓
MessageChannel 对应的 Task
       ↓
onmessage()
       ↓
React继续Render

这里更准确的术语是 Task,而不是简单说"宏任务"。


八、为什么不是 requestIdleCallback?

这是一个常见误区。

不能简单说:

❌ React Scheduler 通过 requestIdleCallback 实现时间切片。

因为 React 需要更主动地控制自己的调度,而不能完全依赖浏览器什么时候认为"空闲"。

现代 Scheduler 的核心思路是:

text 复制代码
任务队列
 +
任务优先级
 +
时间切片
 +
异步调度下一轮工作

浏览器环境中历史上使用过 MessageChannel 等方式来安排后续任务。

因此面试时最好说:

Scheduler 会通过异步任务把后续工作重新安排到之后执行,浏览器环境中可以使用 MessageChannel 这样的方式实现,而不是简单依赖 requestIdleCallback。


九、高优先级更新来了之后,怎么切换?

把前面的内容全部串起来。 假设现在 React 正在处理:

text 复制代码
TransitionLane
 ↓
低优先级 Render
 ↓
Fiber A
 ↓
Fiber B
 ↓
Fiber C

突然用户点击按钮:

text 复制代码
产生高优先级更新
 ↓
分配更高优先级 Lane

React 发现:

text 复制代码
当前:
TransitionLane

新任务:
更高优先级 Lane

于是:

text 复制代码
低优先级 Render
      ↓
处理 Fiber
      ↓
发现更高优先级工作
      ↓
停止当前 Render
      ↓
Scheduler 优先安排高优先级工作
      ↓
高优先级 Render
      ↓
Commit

注意:

不是把当前 JavaScript 从中间强行切走。

而是:

text 复制代码
当前工作循环
      ↓
在检查点发现更重要的任务
      ↓
主动停止
      ↓
当前 Task 结束
      ↓
Scheduler 后续优先执行高优先级任务

如果之前的低优先级 Render 还没有 Commit,那么已经算出来的那部分结果可以被丢弃,之后根据最新状态重新计算。


十、Fiber、Lane、Scheduler 三者到底是什么关系?

这是整道题最值得记住的一张图:

text 复制代码
                    Update
                       ↓
                     Lane
             "这个更新有多重要?"
                       ↓
                  Scheduler
             "什么时候执行它?"
                       ↓
               React Work Loop
              "现在做哪些工作?"
                       ↓
                    Fiber
              "具体处理哪个节点?"
                       ↓
                 shouldYield()
                       ↓
              是否需要主动让出?
                  ↙         ↘
                否           是
                ↓             ↓
             继续执行      停止当前工作
                              ↓
                       安排下一次 Task
                              ↓
                       当前 Task 结束
                              ↓
                       浏览器获得机会
                              ↓
                     下一次 Task 执行
                              ↓
                       继续 Fiber 工作

最后压缩成三句话

Lane 管"谁更重要"。
Scheduler 管"什么时候做"。
Fiber 把工作拆开,让 React 能够在工作循环中一段一段地推进。

三者结合起来,才有了 React 的可中断 Render 和优先级调度。

相关推荐
变与不变8065 小时前
jQuery怎么使用
前端·javascript
神膘护体小月半5 小时前
flex 布局的 order 属性
前端·css
paopaokaka_luck5 小时前
流浪动物救助与收养系统(DeepSeek AI对话、 ECharts多维分析、救助流程衔接、领养与回访衔接、 捐赠确认与公示、志愿者协作)
java·前端·javascript·人工智能·mysql·echarts·推荐算法
仰望.5 小时前
vue 表格组件 vxe-table 树表格懒加载支持拖拽排序的详细用法
前端·javascript·vue.js·vxe-table
前端 贾公子5 小时前
vite-plugin-uni-manifest 升级指南
前端
小码哥_常5 小时前
iPhone 用户传了张 HEIC,我的前端崩了
前端
小码哥_常5 小时前
你发的原图可能暴露你家地址——聊聊图片 EXIF 隐私
前端
java1234_小锋6 小时前
【技术专题】CSS3 网页技术 - CSS3 优先级
前端·css·css3
鸿蒙开发7 小时前
共享 Schema 与 SchemaFactory
前端·后端·harmonyos
屏什么录7 小时前
录屏超过两小时就卡顿崩盘?从参数调优到代码实现,一文彻底搞定长时间录屏
前端·音视频开发