Zustand 持久化与中间件
persist 怎样保存并恢复一份状态?
- 存储键: 用 name 区分持久化数据,避免多个 store 写入同一键而互相覆盖;
- 存储适配: createJSONStorage 将满足存储接口的实现包装成 JSON 存储,按需求选择 localStorage、sessionStorage 或异步存储;
- 数据范围: 只持久化需要跨页面或跨启动保留的数据,函数、DOM 与临时请求状态不适合直接存入;
import { create } from "zustand";
import { persist, createJSONStorage } from "zustand/middleware";
export const usePreferences = create(
persist(
(set) => ({
theme: "light",
setTheme: (theme) => set({ theme }),
}),
{
name: "preferences",
storage: createJSONStorage(() => localStorage),
partialize: (state) => ({ theme: state.theme }),
},
),
);
恢复数据时需要处理哪些时序问题?
- 水合完成: 异步存储恢复不是同步初始化,页面在恢复前可能先看到默认值,应按需要显示加载或延迟使用相关状态;
- 服务端环境: 没有 localStorage,避免在服务器执行浏览器存储逻辑,也要防止客户端初值与服务端 HTML 不一致;
- 用户隔离: 切换账户时确认哪些持久化数据应清空或按用户隔离,不把本地残留当作当前用户授权状态;
数据结构变化后怎样兼容旧存储?
- 版本: 通过 version 标识持久化结构,必要时用 migrate 转换旧数据;
- 合并: 明确恢复数据与当前默认值如何合并,嵌套对象尤其要避免浅层合并丢字段;
- 可序列化: 日期、Map 等值需要明确转换策略,JSON 不能保留所有 JavaScript 类型语义;
const persistOptions = {
name: "preferences",
version: 1,
migrate: (stored, version) => {
if (version === 0) {
return { theme: stored.darkMode ? "dark" : "light" };
}
return stored;
},
};
- 输入校验: 实际迁移还应验证旧数据的结构并提供默认值,本地存储可能缺失、损坏或来自更早版本;
其他中间件分别增强什么能力?
| 中间件 | 主要职责 |
|---|---|
| immer | 简化不可变状态更新 |
| devtools | 连接开发工具观察状态与动作 |
| subscribeWithSelector | 为外部订阅选择状态片段 |
- 组合原则: 按实际需要选择并验证组合顺序,不把所有中间件默认加入每个 store;