Reatom:随你成长的状态管理

发布: (2025年12月8日 GMT+8 20:47)
8 min read
原文: Dev.to

Source: Dev.to

碎片化问题

现代前端开发有一个熟悉的模式:

  • 从简单的 useState Hook 开始
  • 需要共享状态?添加 Context
  • Context 重新渲染太频繁?添加状态管理器
  • 状态管理器对异步支持不佳?添加数据获取库
  • 需要表单?再加一个库
  • 路由需要数据加载?使用带 loader 的路由器
  • 持久化?再来一个库
  • 日志和分析?又来了……

每个工具都有自己的思维模型、API 怪癖和体积成本。技术栈不断膨胀,复杂度成倍增长,新成员需要数周才能全部弄懂。跨多个库的版本管理和兼容性维护简直是一场噩梦。

Reatom 是一种不同的思路。 一个连贯的系统可以处理所有这些需求——从最简单的计数器到最复杂的企业级数据流。按需使用,与你已有的技术栈无缝集成。

想快速了解,可访问官网:

四条原则指引 Reatom 的设计:

  • 原语胜过框架 – 少量强大的构建块胜过复杂的框架。
  • 组合胜于配置 – 堆叠简单的部件,而不是配置单体。
  • 明确具体,隐式通用 – 清晰关注重要的,隐藏样板代码。
  • 兼容性值得复杂度 – 与你的技术栈兼容,而不是对立。

这些并非营销口号。它们是六年生产使用和持续打磨的结果,始于 2019 年 12 月的首个 LTS 版本。

简单起步

如果你熟悉 signals,Reatom 会感觉很自然:

import { atom, computed } from '@reatom/core'

const counter = atom(0)
const isEven = computed(() => counter() % 2 === 0)

// Read
console.log(counter()) // 0
console.log(isEven()) // true

// Write
counter.set(5)
counter.set(prev => prev + 1)

没有 providers,没有样板代码,没有繁琐的仪式感。核心体积 小于 3 KB(gzip) —— 比一些所谓的“轻量”方案还要小。

你可以止步于此,但真正有趣的地方在后面……

无限扩展

同样的原语可以支撑企业级的复杂度:

import {
  atom,
  computed,
  withAsyncData,
  withSearchParams,
} from '@reatom/core'

// Sync with URL search params automatically
const search = atom('', 'search').extend(withSearchParams('search'))
const page = atom(1, 'page').extend(withSearchParams('page'))

// Async computed with automatic cancellation
const searchResults = computed(async () => {
  await wrap(sleep(300)) // debounce

  const response = await wrap(fetch(`/api/search?q=${search()}&page=${page()}`))
  return await wrap(response.json())
}, 'searchResults').extend(withAsyncData({ initState: [] }))

// Usage
searchResults.ready() // loading state
searchResults.data()   // the results
searchResults.error()  // any errors

这里发生了什么

  • searchpage atom 自动与 URL 参数同步。
  • 计算属性仅在依赖变化 有订阅时重新运行(惰性)。
  • 如果用户输入速度快于 API 响应,之前的请求会自动取消,消除竞争条件和内存泄漏。

这正是计数器示例中使用的同一个 atom——只是被扩展了。

扩展系统

与其使用单体框架,Reatom 提供可组合的扩展,能够干净地堆叠:

const theme = atom('dark').extend(
  // Persist to localStorage
  withLocalStorage('theme')
)

const list = atom([]).extend(
  // Memoize equal changes
  withMemo()
)

const unreadCount = atom(0).extend(
  // React to state changes
  withChangeHook(count => {
    document.title = count > 0 ? `(${count}) My App` : 'My App'
  }),
  // Sync across tabs
  withBroadcastChannel('notificationsCounter')
)

每个扩展专注于一件事。只组合你真正需要的功能。

超越状态:完整解决方案

表单

类型安全的表单管理,提供一流支持,包括:

  • 字段级和表单级验证(同步和异步)
  • 与标准模式验证器集成(Zod、Valibot 等)
  • 动态字段数组,支持添加、删除、重新排序操作
  • 焦点跟踪、脏状态检测、错误管理
  • 无奇怪的字符串路径——只使用对象和 atom
  • 极度优化且性能卓越

路由

类型安全的路由,具备自动生命周期管理:

  • 参数验证与转换
  • 数据加载,导航时自动取消
  • 嵌套路由与共享布局
  • 搜索参数处理
  • 同构跨框架支持

工厂模式——在路由 loader 中创建的状态会在导航时自动回收,解决“全局状态清理”问题。

持久化

大量内置存储适配器,提供高级特性:

  • localStoragesessionStorageBroadcastChannel、IndexedDB、Cookie 和 Cookie Store
  • 与标准模式验证器集成(Zod、Valibot 等)
  • 数据格式变更的版本迁移
  • TTL(生存时间)支持
  • 当不可用时优雅回退到内存存储

深层技术优势

原因追踪

Reatom 模拟 TC39 AsyncContext,因此获得诸多好处:

  • 自动取消并发的异步链(类似防抖)
  • 一些 IoC/DI 的可能性
  • 异步事务
  • 过程追踪与日志记录

最后一项特性是复杂异步流的游戏规则改变者。你可以轻松检查并发操作的根因,将调试时间从数小时降至数分钟。

性能

在功能集如此丰富的前提下,Reatom 仍然提供顶级性能。应用越复杂,Reatom 相较于其他方案的优势越明显。针对复杂计算的基准测试显示,Reatom 在中等规模的依赖图上跑赢 MobX——这在 Reatom 使用不可变数据结构并在独立的 async context 中运行的情况下尤为惊人——这些特性通常会带来额外开销。

明确的响应式,无 Proxy

基于 Proxy 的响应式在简单场景下很便利,但在大型代码库中难以追踪。使用 Reatom 时,将鼠标悬停在任意属性上即可看到类型提示——始终清晰哪部分是响应式的。

Reatom 鼓励 atom 化——将后端 DTO 转换为应用模型,令可变属性成为 atom:

// Backend DTO
type UserDto = { id: string; name: string }

// Application model with explicit reactivity
type User = { id: string; name: Atom }

const userModel = { id: dto.id, name: atom(dto.name) }

// Usage
userModel.id          // static value — no reactivity
userModel.name()      // reactive — tracks changes

这种模式提供细粒度控制:在需要的地方精确定义响应式元素,避免不受控的 observer 创建,并保持响应式图谱的显式化。

Back to Blog

相关文章

阅读更多 »