中间件系统
中间件是 包一层再包一层的 StateCreator:在不改 Vanilla 核心的前提下增强 set / get / store。入口类型在 src/middleware.ts,各中间件在 src/middleware/*.ts。
1. 组合模型
text
create( devtools( persist( immer( (set, get, store) => ({ ... }) ) ) ) )执行顺序:由内到外
- 最内层:用户
StateCreator定义 state 与 actions。 - 向外:每层中间件包装上一层的
set/get(如 Immer 改set语义,persist 在 set 后写 storage,devtools 记录 action)。
记忆:先写的中间件离 state 最近,最外层最先接到 create 调用。
| 需求 | 常见顺序(内 → 外) |
|---|---|
| 不可变 + 持久化 + 调试 | immer → persist → devtools |
2. 常见中间件职责
| 中间件 | 作用 | 注意 |
|---|---|---|
| immer | set 接收 draft,深层修改 | 与「手写展开」二选一即可 |
| persist | 序列化到 localStorage 等 | partialize 控制字段;注意 SSR 与水合 |
| devtools | 对接 Redux DevTools | 生产关闭 |
| redux | 兼容 reducer + dispatch 形态 | 迁移 Redux 时用 |
| subscribeWithSelector | subscribe(selector, listener) | 非 React 侧细粒度监听 |
| combine | 合并多个 slice 初始状态与 actions | 大 store 拆分 |
不必背诵各文件实现;抓住「拦截 set/get」即可推断行为。
3. 类型与 mutator
TypeScript 下中间件通过 StoreMutatorIdentifier 把增强能力(如 persist 的 persist API)合并进 store 类型。多个中间件时类型从左到右(或按库定义)折叠。
若类型报错,优先查官方文档的 middleware 组合示例,而非手写 any 绕过。
4. 自定义中间件(思路)
自定义中间件 = (config) => (set, get, api) => config( wrappedSet, get, api ):
- 在
wrappedSet里:日志、埋点、节流、乐观更新写回。 - 避免无限递归:始终调用「内层 set」而非自己。
5. 性能与陷阱
- 每层中间件增加一次函数调用;生产只保留必要层。
- persist 同步 IO 可能卡主线程;大状态考虑异步存储或拆分。
- devtools 序列化整个 state 有成本。
6. 总结
中间件 = 高阶 StateCreator;理解组合顺序与 set 拦截点即可排查「为什么 persist 没存」「为什么 DevTools 没动作」。状态合并语义仍由 Vanilla 05-状态更新 决定,Immer 只改变你如何写 set 的参数。