Skip to content
目录

关键设计决策和原因

本文汇总 Zustand 主要设计取舍:为何简单、为何在并发下仍安全、代价是什么。细节实现见各专题章节。


1. 为什么不需要 Provider?

决策create() 得到模块级 store + Hook,无 Context Provider。

收益代价
API 少一层包裹、任意组件直接 useStore默认单例,难以「同页两套 store」除非多 create 或自建 Context
避免 Context value 变化导致大范围重渲染测试需 mock 模块或注入 Vanilla store

性能:订阅走 store 的 Set,不是 Context 自上而下传播;配合选择器只更新相关组件(07-性能优化)。

需要多实例时createStore + React Context 传 api,或按路由/租户拆分模块。


2. 为什么使用 useSyncExternalStore?

决策:React 集成仅用官方外部 store Hook,不用 useState + useEffect 订阅。

原因:并发下 tearing、僵尸子组件、SSR 快照不一致(06-并发)。

权衡:要求 React 18+(或 backport 包);行为与 React 调度绑定,自定义订阅需理解 snapshot 语义。


3. 为什么默认浅合并?

决策set(partial) 顶层 Object.assign,非深合并。

收益代价
实现小、语义可预测、性能好嵌套对象需手动展开或 Immer

与 Redux reducer 常返回新顶层对象的习惯一致;深合并易引发「意外保留旧嵌套字段」与性能问题。


4. 为什么选择「推荐单一 Store」而非强制原子化?

决策:库不强制 Jotai/Recoil 式原子,但推荐按领域组织一个或少量 store。

单一 / 少量 store多 store / slices
简单应用、全局 UI+用户大应用、按域拆分 bundle 与订阅
actions 跨字段方便combine 或拆文件 slices

灵活性留给用户;不像 Redux 强制单一 reducer 树那么重。


5. 中间件为何是「洋葱模型」?

决策:中间件 = 高阶 StateCreator,由内向外包装 set/get

收益代价
核心零依赖;persist/devtools 可选组合顺序需理解;类型折叠复杂

04-中间件系统


6. 为什么用 Object.is 做快照比较?

决策:Vanilla 层 state 变更用 Object.is;React 层 snapshot 比较同样依赖 Object.is

  • 与 React 18 协调语义一致。
  • 选择器应返回稳定引用(useShallow)避免「值没变、引用变」导致重渲染。

7. 为什么 create 返回的是 Hook?

决策const useStore = create(...) 符合 React 习惯,且隐藏 store api 传递。

收益代价
符合 hooks 规则、Tree-shaking 友好不能在不遵守 hooks 规则的地方调用

底层仍是 Vanilla StoreApiuseStore(api) 也可用于 Context 注入场景。


8. 为什么同时提供 Vanilla Store?

决策createStorecreate 并存。

场景:非 React 环境、测试、在订阅里驱动非 React UI、SSR 脚本、与 React 无关的 worker。

原则一套状态、多种订阅面——React 只是订阅者之一。


总结

主题一句话
无 Provider全局 store + 直接订阅,换简单与细粒度
useSyncExternalStore并发与 SSR 正确性
浅合并小而快,嵌套用 Immer/展开
中间件可选增强,不膨胀核心
Hook 形态贴合 React,底层仍 Vanilla

延伸阅读

MIT License.