react的事件优先级

在 React 18(以及 React 17 的重构版本)中,优先级的概念被具象化为 "Lanes"(车道)模型

React 内部使用一套复杂的**位运算(Bitmask)**来管理这些优先级。为了方便理解,我们可以将它们映射为 Scheduler(调度器) 的 5 个核心等级。

理解这些优先级,是你掌握 React 并发机制(Concurrent Features)和性能调优的钥匙。

以下是按照优先级从高到低的详细分类:


1. Immediate / Synchronous Priority (立即/同步优先级)

对应的 Lane: SyncLane 权重: 最高级(No.1) 不可中断

  • 定义: 必须立即完成的任务。如果不立即更新,用户会觉得"坏了"或"没反应"。这种更新会绕过 React 的时间切片机制,强制同步执行(类似于 React 15 的行为)。
  • 触发场景:
    • 离散的用户交互: click (点击), input (输入), keydown (按键), touchstart
    • 强制同步 API: flushSync(() => { ... })
  • 行为:
    • 一旦触发,React 会锁死主线程,直到 DOM 更新完成。
    • 如果在 click 事件中写了死循环,浏览器会直接卡死。
  • 过期时间: -1ms(立刻过期,必须马上做)。

2. User Blocking Priority (用户阻塞优先级)

对应的 Lane: InputContinuousLane 权重: 次高级(No.2) 短时间可中断,但必须尽快

  • 定义: 同样是用户交互,但属于连续触发的类型。虽然需要高频响应,但允许极短的延迟(为了防止连续触发导致 JS 线程完全堵塞)。
  • 触发场景:
    • scroll (滚动)
    • mousemove (鼠标移动)
    • drag (拖拽)
    • wheel (滚轮)
  • 行为:
    • React 会尝试尽快处理,但如果短时间内触发太多,React 会进行一定的合并(Batching)。
    • 它比点击事件低一级,是因为:用户滚动时,如果画面有一丢丢(比如几毫秒)的跟手延迟,通常是可以接受的,但点击按钮没反应是不能接受的。
  • 过期时间: 250ms(如果排队超过 250ms 还没执行,就会强制升级为同步任务立即执行)。

3. Normal Priority (普通/默认优先级)

对应的 Lane: DefaultLane 权重: 中级(No.3) 可中断

  • 定义: React 中的默认优先级。如果你没有特殊操作,绝大多数状态更新都落在这个桶里。
  • 触发场景:
    • useEffect 里发起的更新。
    • 网络请求(fetch, axios)回调里的 setState
    • setTimeout / setInterval 里的更新。
    • 自定义事件监听器。
  • 行为:
    • 并发模式的主战场: 这里的任务会利用 Time Slicing(时间切片)
    • React 会给每个任务分配 5ms 的时间片。如果 5ms 没做完,就暂停,把主线程还给浏览器去画帧,下一帧再继续。
  • 过期时间: 5000ms(5秒)。这意味着即使很卡,5秒后 React 也会强制把这个任务执行完,保证界面最终会更新。

4. Low Priority (低优先级)

对应的 Lane: TransitionLane (React 18 新增) 权重: 低级(No.4) 随时可被中断、可被废弃

  • 定义: 明确告诉 React "我不着急"的任务。这是 React 18 并发特性的核心。
  • 触发场景:
    • startTransition 包裹的更新。
    • useDeferredValue 产生的更新。
    • Suspense 的重试机制。
  • 行为:
    • "备胎": 只要有更高优先级的任务(比如用户点了一下鼠标),正在运行的 Low Priority 任务就会立刻被暂停,甚至直接扔掉。
    • 无 Loading: 在 Transition 期间,React 会保留旧的 UI,在后台悄悄计算新 UI,算好了再一次性切换。
  • 典型用例: 搜索框下方的长列表过滤、Tab 切换时的复杂页面渲染。

5. Idle Priority (空闲优先级)

对应的 Lane: IdleLane 权重: 最低级(No.5)

  • 定义: 只有当浏览器完全没事干(主线程空闲)时才会执行的任务。
  • 触发场景:
    • 通常用于 Offscreen(离屏) 内容的预渲染。
    • 比如:隐藏的 Tab 页、视口之外的组件预热。
  • 行为:
    • 优先级无限低,可能永远不会被执行(如果一直忙的话)。
    • React 内部其实还没大规模暴露这个给开发者直接用,主要在框架内部使用(如 BeforeContentOffscreenComponent)。

总结图谱

为了方便记忆,你可以想象成医院的急诊分诊台

优先级名称 对应交互 医院比喻 行为特点
Immediate click, input, flushSync 心脏骤停 (抢救) 医生放下所有事,立刻处理,不能等。
User Blocking scroll, drag 骨折/外伤 (急诊) 很痛,要尽快看,但稍微排几分钟队死不了人。
Normal fetch, setTimeout 发烧/感冒 (普通门诊) 正常排队,医生看一会儿可能会去接个急诊电话。
Low startTransition 常规体检 随时可以被插队,今天人多就明天再来。
Idle Offscreen 医院保洁 没病人的时候打扫卫生。

为什么你要知道这些?

  1. 性能调试: 当你发现输入框打字卡顿时,你要反应过来:是不是混入了大量的计算逻辑?是不是应该把列表过滤的计算降级为 Low Priority(使用 useDeferredValue)?
  2. 避免滥用: 以前为了优化,很多开发者喜欢用 setTimeout(..., 0) 来把任务往后排。现在你知道了,setTimeout 属于 Normal 优先级,而 startTransition 属于更低的 Low 优先级,后者在 React 18 中通常是更好的选择,因为它支持中断。
  3. 理解 batchedUpdates 只有同一优先级的更新才会被合并。高优先级会打断低优先级的合并。
相关推荐
jingling55512 小时前
css进阶 | 实现罐子中的水流搅拌效果
前端·css
悟能不能悟13 小时前
前端上载文件时,上载多个文件,但是一个一个调用接口,怎么实现
前端
可问春风_ren14 小时前
前端文件上传详细解析
前端·ecmascript·reactjs·js
羊小猪~~14 小时前
【QT】--文件操作
前端·数据库·c++·后端·qt·qt6.3
晚风资源组15 小时前
CSS文字和图片在容器内垂直居中的简单方法
前端·css·css3
Miketutu16 小时前
Flutter学习 - 组件通信与网络请求Dio
开发语言·前端·javascript
光影少年17 小时前
前端如何调用gpu渲染,提升gpu渲染
前端·aigc·web·ai编程
Surplusx18 小时前
运用VS Code前端开发工具完成网页头部导航栏
前端·html
小宇的天下18 小时前
Calibre 3Dstack --每日一个命令day13【enclosure】(3-13)
服务器·前端·数据库
一只小bit19 小时前
Qt 文件:QFile 文件读写与管理教程
前端·c++·qt·gui