Jotai v2.20 重整 Store 内核:优化高频更新,并为 v3 铺路

2026-07-24 35 预计阅读时间: 1 分钟
来源: infoq.com AI 摘要 Original link

Disclaimer: This article is an AI-assisted summary. Read it together with the original source when precision matters. The summary may omit context, version differences, or edge cases and is not official documentation.

预计阅读时间:7 分钟

Jotai v2.20.0 的重点不是增加一批面向业务的新 API,而是重整 Store 的内部构建模块,修复此前出现的性能回退,并改善高吞吐场景下的执行效率。普通 React 应用可以继续使用熟悉的 atom、useAtom 和 Provider;真正需要仔细检查升级影响的,是封装 Jotai Store、扩展底层能力或向其他项目提供状态工具的库作者。

为什么高吞吐场景更考验 Store

在表单、行情面板、协同编辑、实时监控和拖拽交互中,状态更新可能连续发生。此时,单次更新快几微秒并不足以说明问题,关键在于整条更新链路是否稳定:

  • Store 写入 atom 时需要维护依赖关系。
  • 派生 atom 需要判断是否重新计算。
  • 订阅者需要收到通知,但不应触发无关组件渲染。
  • 批量或连续写入不能产生过多中间工作。

因此,内部构建模块的调整即使不改变公开 API,也可能明显影响真实应用的尾延迟、CPU 占用和渲染次数。v2.20 的方向正是改善这类高频工作负载,并处理先前版本中的性能回退。

需要注意的是,“吞吐提升”不等于所有页面都会自动变快。如果瓶颈来自昂贵的 React 组件、每次渲染都创建新对象,或粒度过大的全局 atom,仅升级依赖不会消除这些成本。

日常业务代码基本不需要改写

对于只使用公开 React API 的项目,升级可以从常规依赖更新开始:

npm install jotai@2.20.0
npm test
npm run build

下面是一个可以直接放进 Vite React 项目的高频计数示例。它通过 requestAnimationFrame 合并一帧内的多次增量,既能用于验证升级,也展示了一种适合高频输入的实践方式。

// src/App.jsx
import { useEffect, useRef, useState } from 'react'
import { atom, useAtomValue, useSetAtom } from 'jotai'

const eventCountAtom = atom(0)
const addEventsAtom = atom(null, (get, set, amount) => {
  set(eventCountAtom, get(eventCountAtom) + amount)
})

function Counter() {
  const count = useAtomValue(eventCountAtom)
  return <strong>Processed events: {count.toLocaleString()}</strong>
}

export default function App() {
  const addEvents = useSetAtom(addEventsAtom)
  const pendingRef = useRef(0)
  const frameRef = useRef(null)
  const [running, setRunning] = useState(false)

  useEffect(() => {
    if (!running) return

    const timer = setInterval(() => {
      pendingRef.current += 100

      if (frameRef.current === null) {
        frameRef.current = requestAnimationFrame(() => {
          addEvents(pendingRef.current)
          pendingRef.current = 0
          frameRef.current = null
        })
      }
    }, 10)

    return () => {
      clearInterval(timer)
      if (frameRef.current !== null) cancelAnimationFrame(frameRef.current)
      frameRef.current = null
      pendingRef.current = 0
    }
  }, [addEvents, running])

  return (
    <main>
      <Counter />
      <button onClick={() => setRunning((value) => !value)}>
        {running ? 'Stop' : 'Start'} stream
      </button>
    </main>
  )
}

这里有两个值得保留的设计:读取使用 useAtomValue,写入使用 useSetAtom,避免写入端订阅它并不需要展示的状态;同时,将大量外部事件压缩为每帧一次 atom 写入。v2.20 优化的是底层执行路径,应用仍应控制订阅范围和更新频率。

升级时不要只看平均耗时

可以这样实践升级验证:在同一台机器、同一构建模式和同一测试数据下,对升级前后版本分别记录提交次数、长任务数量和交互延迟。React Profiler 适合观察组件提交,浏览器 Performance 面板则适合定位脚本执行和主线程阻塞。

建议至少覆盖三类负载:

  1. 连续写入同一个 atom,例如进度、鼠标位置或流式响应。
  2. 同时更新多个 atom,例如批量表单或实时数据表格。
  3. 更新基础 atom 后触发多层派生 atom 重新计算。

不要只运行开发构建。React 开发模式、Strict Mode 和调试工具会引入额外工作,应同时测量生产构建:

npm run build
npm run preview

如果升级后仍出现卡顿,应继续检查派生计算是否过重、atom 返回值是否保持引用稳定,以及组件是否订阅了超出展示需要的状态。

库作者需要提前处理 v3 信号

v2.20 已开始通过弃用项为 Jotai v3 做准备。来源摘要没有列出具体弃用符号,因此不应凭空假设迁移名称;更可靠的做法是让 TypeScript、构建日志和当前版本的类型声明指出实际使用位置。

库作者可以执行以下检查:

npm install jotai@2.20.0
npx tsc --noEmit
npm test
npm run build

随后搜索项目中对内部入口、非公开类型和 Store 底层构件的直接引用:

rg "jotai/(vanilla|react)|createStore|getDefaultStore|INTERNAL|unstable" src test

这条命令只是审计入口,并不表示其中每个 API 都已弃用。判断依据应是 v2.20 实际导出的类型提示和弃用标记。若维护的是公共库,还应测试 Provider 内外、多个 Store 实例、SSR、并发渲染和多个 Jotai 副本共存等边界情况。

采用建议

普通应用可以把 v2.20 视为一次 API 表面稳定、内部变化较大的性能升级:锁定版本,运行现有测试,再用真实高频页面做一次生产构建测量。不要为了追逐内部优化而重写已经清晰的 atom 结构。

库作者则应把这次升级当作 v3 前的迁移窗口:清理内部 API 依赖,响应弃用提示,补齐 Store 行为测试,并确认 peerDependencies 范围。这样既能获得 v2.20 对高吞吐工作负载的改进,也能减少未来主版本升级时的集中风险。


相关推荐