Reatom:随你成长的状态管理
Source: Dev.to
碎片化问题
现代前端开发有一个熟悉的模式:
- 从简单的
useStateHook 开始 - 需要共享状态?添加 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
这里发生了什么
search和pageatom 自动与 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 中创建的状态会在导航时自动回收,解决“全局状态清理”问题。
持久化
大量内置存储适配器,提供高级特性:
localStorage、sessionStorage、BroadcastChannel、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 创建,并保持响应式图谱的显式化。