Skip to content
目录

Vanilla Store 实现详解

Vanilla Store(createStore)是 Zustand 的核心:不依赖 React,用闭包 + 订阅集合实现最小状态容器。create() 只是在 Store API 外包一层 useStore Hook。

源码入口:src/vanilla.ts


1. Store API 一览

方法作用
getState()读当前状态(同步、非响应式)
setState(partial, replace?)写状态并通知订阅者
subscribe(listener)listener(state, prevState);返回取消函数
getInitialState()返回创建时的初始快照(SSR / 水合对齐用)

2. createStore 心智模型

闭包变量 state
监听器集合 listeners: Set<Listener>
createState(set, get, api)  → 初始化 state
setState → 合并/替换 → 若引用变则 listeners.forEach

无 Redux 的 reducer 管道、无 Proxy(除非中间件加 Immer);一次 set 一次通知轮次


3. setState 语义

3.1 两种写入形态

形态调用行为
部分更新(默认)set({ count: 1 })set(s => ({ ... }))浅合并进现有 state
完全替换set(newState, true)丢弃旧顶层,整体替换

函数式更新:set((state) => partial),与 React setState 类似,便于基于上一拍计算。

3.2 何时不通知

若合并/替换后 Object.is(nextState, prevState) 为真,则不遍历 listeners。因此返回同一引用或结构未变的更新不会触发订阅(与 React 快照短路一致)。

3.3 浅合并规则

默认对顶层 keyObject.assign({}, state, partial)

  • 更新 { count: 1 } 不影响同层其他 key。
  • 更新 { user: newUser }整键替换 user,不会深合并 user.name

嵌套结构需:手动展开、immer 中间件,或不可变工具函数。


4. subscribe

  • 使用 Set<Listener> 存储,添加/删除 O(1)。
  • 每次 setState 成功变更后,同步调用所有 listener(无内置批量合并)。
  • 返回 () => listeners.delete(listener) 用于清理。

React 层的 useSyncExternalStore 会把 subscribe 与组件生命周期绑定。


5. 初始化流程

  1. 创建空 apisetState / getState / subscribe / getInitialState)。
  2. 调用用户 createState(set, get, api),返回值作为初始 state
  3. getInitialState 固定指向该初始快照。

create((set, get) => ({ ... })) 里的 get 可在 action 中读最新 state,避免闭包陈旧(仍须在事件/异步回调里调用,而非 render 期间副作用滥用)。


6. 与 React 层的关系

VanillaReact create
createStorecreate = createStore + 绑定 useStore
任意环境可用组件内 useStore(api, selector)

同一 store 实例可在 React 组件外 getState / subscribe,适合与路由、Web Worker 消息桥接。


7. 阅读源码时看什么

  1. createStoreImplsetState 合并分支与 Object.is 短路。
  2. replace 参数与「非对象 partial」的替换路径。
  3. 中间件如何包装 set/get(见 04-中间件系统)。

不必背诵完整实现;理解 闭包 state + Set 订阅 + 浅合并 即可推导大部分行为。


8. 总结

Vanilla Store 刻意保持极小表面:写、读、订阅、初始快照。性能与并发语义主要由 React 层的 useSyncExternalStore 与选择器策略决定(03-React集成07-性能优化)。

MIT License.