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 只有同一优先级的更新才会被合并。高优先级会打断低优先级的合并。
相关推荐
soda_yo1 小时前
浅拷贝与深拷贝: 克隆一只哈基米
前端·javascript·面试
冴羽1 小时前
Nano Banana Pro 零基础快速上手
前端·人工智能·aigc
幼儿园技术家1 小时前
浏览器加载html、css、js的顺序
前端
爱分享的鱼鱼2 小时前
Vue生命周期钩子详解与实战应用
前端·vue.js
晴殇i2 小时前
CSS Grid 与 Flexbox:现代前端布局的双子星
前端·css
曹卫平dudu2 小时前
一起学习TailWind Css
前端·css
sosojie2 小时前
and+design的table前端本地分页处理
前端·vue.js
炫饭第一名2 小时前
前端玩转 AI 应用开发|SSE 协议与JS中的流式处理🌊
前端·人工智能·程序员
前端老宋Running2 小时前
一种名为“Webpack 配置工程师”的已故职业—— Vite 与“零配置”的快乐
前端·vite·前端工程化