187 - 《蚂蚁中后台数据流的新选型》
我们的数据流方案要做一次迭代,以下是做了些调研后的整理,暂未包含和 Umi 框架结合的部分。
背景知识:数据流 2022
先说结论:目前个人的倾向是,基于 Valtio 浅封一个数据流方案集成到 Umi 中。
为啥是 valtio?
valtio 的特点是外部多 Store + 基于 Proxy。1)个人用不惯 jotai 和 recoil 这种 react 内部原子化的数据流方案,感觉和被 redux 培养起来的心智模型有冲突,所以会更倾向于外部 Store 一些,2)由于场景是中后台,对于兼容性的容忍度比较高,比如不用兼容 IE11,所以完全可以用基于 Proxy 的数据流方案,这类数据流方案在更新数据和读取数据时都更简单。
为啥不是 zustand?
zustand 和 valtio 是同一个作者写的,功能覆盖上其实比较类似,最大的区别是 valtio 基于 proxy 而 zustand 不是。个人有几个方面的考虑,1)更新数据,2)读取数据,3)类型提示。
更新数据的方式更符合人性,比如可以直接 state.todos['321'].completed = true
,而不用 setState(todos => ({ …todos, 321: { …todos['321'], completed: true } }))
。当然,这一点非 proxy 方案可基于 immer 实现和 proxy 方案类似的操作。
读取数据默认高性能,无需 selector。非 proxy 方案比如 react-redux 和 zustand 为了性能优化,避免不必要的 re-render,通常会通过 selector 选择 store 的一部分,这会带来不必要的心智负担。基于 Proxy 的方案是响应式的,无需 selector,默认高性能。
类型提示的差异主要在扩展上。valtio 用的是组合式,zustand 用的是 middleware。没具体试过 zustand 的 middleware,但个人理解,理论上 middleware 的类型提示没有 valtio 友好。比如 valtio 的 proxyWithHistory 会把数据结构改成 { value, history, redo, undo, … }
这种,在类型提示上可以完美衔接。
为啥要封一层而不直接用?
有几个考虑。
1、valtio 未来肯定会迭代,加一层封装能抹平大版本更新导致的可能的 break change,方便做应用治理
2、调整实现。比如包括,1)约束接口;2)封装更多符合蚂蚁需求的扩展,比如 persistant storage 扩展、log 扩展、auto loading state 扩展等;3)支持全局生效的扩展。
3、便于和框架结合,在使用、性能等方面都有更优表现,没具体展开想过,目前想到的比如基于路由的按需、store 微生成器、配置式的扩展、和请求方案的结合等
valtio 的功能如何?
数据流方案应该关注啥? 整理之后发现还是有不少的。比如心智模型、读取数据、写入数据、数据推导、异步 Action、渲染性能优化、Suspense 并发模式支持、SSR 支持、React 之外访问、组件封装、瞬时更新、插件中间件扩展、Redux DevTools 支持、兼容性、多实例和单实例、数据序列化能力、同步/异步更新、内存管理、测试、包尺寸等。(还有啥?)大部分数据流方案都考虑了这些点,区别是实现方式和使用体验上的差异。
基本用法
极其简单。
// 1、定义数据
const state = proxy({ count: 0 });
// 2、使用数据
const snap = useSnapshot(state);
snap.count;
// 3、更新数据
state.count += 1;
React 外访问
天然支持。
const state = proxy({ count: 0 });
state.count;
state.count += 1;
数据推导
const state = proxyWithComputed({
count: 0,
}, {
double: snap => snap * 2,
});
Action 和异步 Action
两种用法,可以和 state 放一起,也可以分开。
// 方法一:放一起
const state = proxy({
count: 0,
actions: {
add() {
// 注意这里别用 this.count,基于 snap 调用时会报错
state.count += 1;
},
}
});
// 方法二:分开