Skip to content
目录

性能优化策略

Zustand 的性能优势主要来自:按选择器订阅 + 引用相等短路 + 无 Provider 的 Context 开销。本节从使用侧和原理侧说明如何少渲染、少计算、少持久化开销。


1. 核心机制回顾

机制作用
选择器 useStore(s => s.x)仅当 x 的快照变化时触发该组件重渲染
Object.is 比较快照与上一拍 getSnapshot() 相同则跳过渲染
外部 store + useSyncExternalStore避免整树 Context 广播

性能问题几乎都来自:选择器每次返回新引用,或 订阅了过大的状态切片


2. 细粒度订阅

2.1 只选需要的字段

typescript
const bears = useStore((s) => s.bears) // ✅ 仅 bears 变才更新

避免 useStore() 无选择器——等价于订阅整个 state,任意字段变化都会重渲染。

2.2 多字段:用 useShallow

选择器若返回新对象/新数组(如 { count, text }[a, b]),每次 getSnapshot 引用都变,会每帧重渲染甚至触发「Maximum update depth」类问题。

做法:对「多字段打包」使用 useShallow,内部对上一拍结果做浅比较,内容不变则返回旧引用,与 Object.is 短路配合。

写法结果
(s) => ({ a: s.a, b: s.b }) 无 shallow几乎每次新对象 → 频繁更新
useShallow((s) => ({ a: s.a, b: s.b }))a/b 浅层变化时更新

原理(一句话)useRef 存上一拍 + shallow 比较;相等则返回缓存引用,不等则更新 ref。

2.3 非 React 环境:subscribeWithSelector

在组件外(模块、路由守卫、图表实例)用 subscribeWithSelector 中间件,对 (state) => slice 订阅,只有 slice 变化才执行 listener,避免全量 subscribe 回调。


3. 中间件与运行时开销

中间件优化建议
devtools生产环境 enabled: false
persistpartialize 只持久化必要字段;大对象注意序列化成本
immer适合深层嵌套更新;结构共享减少无效变更,但仍有 proxy 开销
多层嵌套只组合需要的中间件,顺序影响 set 语义(见 04-中间件

4. 应用结构层

4.1 拆分 Store

单一巨大 store 会导致:

  • 任意更新使「全量订阅」组件全部刷新;
  • 选择器虽可切片,但 DevTools / persist 范围过大。

按领域拆 store(user / cart / ui)或 slices + combine,让订阅面与持久化面自然变小。

4.2 组件层

  • React.memo 子组件 + 父组件只传从 store 选出的原始值或稳定引用。
  • 避免在 useStore 选择器里创建 JSX 或内联对象 props。

5. 批量更新

同一事件处理函数内多次 set

  • Vanilla 层每次 setState 会通知 listener;
  • React 18 自动批处理 多次重渲染为一次(与 React 批处理 一致)。

若需合并为一次通知,可在业务层先算好一次 set(partial),或在中间件里合并(高级用法)。


6. 区分 state 与 actions(结构层)

放在 state放在 actions(或模块函数)
要触发 UI 更新的数据调 API、读写 localStorage
可被选择器订阅的字段不进入 store 的纯工具函数

好处:选择器只订阅 state 时,action 引用变化不会误触发重渲染;partialize 持久化时可只存 state。Setup store 用 ref/state 存数据、普通函数写逻辑即可。

7. 排查清单

  1. React DevTools Profiler:是否因父级大选择器导致子树全刷?
  2. 选择器是否返回新对象/数组?→ useShallow 或拆订阅。
  3. 是否误用全量 useStore()
  4. persist / devtools 是否在生产仍开启?
  5. v5 升级后无限循环?→ 见 09-常见问题 与官方 Discussion #2790。

8. 总结

策略要点
细粒度订阅选择器只取必要字段
稳定引用多字段用 useShallow
结构拆 store、memo 子树
中间件生产关 devtools、persist 部分持久化
并发依赖 useSyncExternalStore,勿回退到 useState+useEffect 订阅

Zustand 本身很轻;性能问题多数是订阅粒度引用稳定性,而非库内部瓶颈。

MIT License.