zustand持久化

Zustand 提供了极简的持久化方案 ,核心通过内置的 persist 中间件实现,无需额外依赖(或仅需少量配置),支持 localStorage/sessionStorage/ 自定义存储(如 React Native 的 AsyncStorage)。以下是完整的持久化教程,包含基础用法、高级配置、常见问题解决。

一、基础用法(最常用)

1. 安装依赖(若需类型支持)

Zustand 核心包已内置 persist 中间件,无需额外安装,仅需确保 zustand 版本 ≥4.0.0:

bash 复制代码
npm install zustand # 或 yarn add zustand

2. 基础持久化配置

tsx 复制代码
import { create } from 'zustand';
import { persist } from 'zustand/middleware'; // 导入持久化中间件

// 创建带持久化的 store
const useCounterStore = create(
  persist(
    // 第一个参数:原始 store 逻辑
    (set) => ({
      count: 0,
      increment: () => set((state) => ({ count: state.count + 1 })),
      decrement: () => set((state) => ({ count: state.count - 1 })),
      reset: () => set({ count: 0 }),
    }),
    // 第二个参数:持久化配置
    {
      name: 'counter-storage', // 存储的 key(必选,用于 localStorage 标识)
      // 默认存储到 localStorage,可指定为 sessionStorage:
      // storage: sessionStorage,
    }
  )
);

// 组件中使用(和普通 zustand 用法完全一致)
function Counter() {
  const { count, increment, reset } = useCounterStore();
  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={increment}>+</button>
      <button onClick={reset}>重置</button>
    </div>
  );
}

✅ 效果:刷新页面后,count 状态会从 localStorage 中恢复,无需额外代码。

二、高级配置(覆盖常见场景)

1. 自定义存储方式(如 React Native)

默认存储是 localStorage(Web),若需适配 React Native,可指定 AsyncStorage

tsx 复制代码
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
import AsyncStorage from '@react-native-async-storage/async-storage'; // RN 持久化存储

const useUserStore = create(
  persist(
    (set) => ({
      userInfo: { name: '', token: '' },
      setUser: (user) => set({ userInfo: user }),
    }),
    {
      name: 'user-storage',
      storage: { // 自定义存储适配器
        getItem: async (name) => {
          const value = await AsyncStorage.getItem(name);
          return value ? JSON.parse(value) : null;
        },
        setItem: async (name, value) => {
          await AsyncStorage.setItem(name, JSON.stringify(value));
        },
        removeItem: async (name) => {
          await AsyncStorage.removeItem(name);
        },
      },
    }
  )
);

3. 持久化加密(敏感数据)

若存储敏感信息(如 token),可通过 serialize/deserialize 加密 / 解密:

tsx 复制代码
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
import CryptoJS from 'crypto-js'; // 需安装:npm install crypto-js

const SECRET_KEY = 'your-secret-key'; // 实际项目中建议从环境变量读取

const useSecureStore = create(
  persist(
    (set) => ({
      token: '',
      setToken: (token) => set({ token }),
    }),
    {
      name: 'secure-storage',
      // 序列化(存储时加密)
      serialize: (state) => {
        const encrypted = CryptoJS.AES.encrypt(
          JSON.stringify(state),
          SECRET_KEY
        ).toString();
        return encrypted;
      },
      // 反序列化(读取时解密)
      deserialize: (encrypted) => {
        try {
          const decrypted = CryptoJS.AES.decrypt(encrypted, SECRET_KEY).toString(
            CryptoJS.enc.Utf8
          );
          return decrypted ? JSON.parse(decrypted) : {};
        } catch (e) {
          return {}; // 解密失败返回空状态
        }
      },
    }
  )
);

4. 手动控制持久化(清除 / 重新加载)

可通过 persist 返回的 persist 对象手动操作存储:

tsx 复制代码
const useStore = create(
  persist(
    (set) => ({
      count: 0,
      increment: () => set((s) => ({ count: s.count + 1 })),
    }),
    { name: 'manual-storage' }
  )
);

// 获取持久化操作方法
const { persist } = useStore;

// 手动清除持久化数据
function clearStorage() {
  persist.clearStorage(); // 清除 localStorage 中的数据
}

// 手动重新加载持久化数据
function reloadStorage() {
  persist.rehydrate(); // 从存储中重新加载状态到 store
}

// 组件中使用
function ControlPanel() {
  return (
    <div>
      <button onClick={clearStorage}>清除持久化数据</button>
      <button onClick={reloadStorage}>重新加载数据</button>
    </div>
  );
}

5. 持久化冲突处理(多标签页同步)

默认情况下,多标签页修改状态可能导致冲突,可通过 merge 配置合并状态:

tsx 复制代码
const useStore = create(
  persist(
    (set) => ({
      count: 0,
    }),
    {
      name: 'sync-storage',
      // 合并多标签页的状态(取最新的 count)
      merge: (persistedState, currentState) => {
        return {
          ...currentState,
          count: persistedState.count > currentState.count ? persistedState.count : currentState.count,
        };
      },
    }
  )
);

三、常见问题解决

1. 状态不持久化?

  • 检查 name 配置是否唯一(避免和其他存储冲突);
  • 确认存储的状态是可序列化的(不能存函数、Symbol 等,persist 会自动忽略不可序列化字段);
  • 若用自定义存储(如 RN AsyncStorage),检查是否安装并链接依赖。

2. 持久化后状态更新不生效?

  • 确保修改状态时使用 set 方法(而非直接赋值);
  • 若手动修改了 localStorage,需调用 persist.rehydrate() 重新加载。

3. 类型报错(TypeScript)?

persist 显式指定状态类型

tsx 复制代码
interface CounterState {
  count: number;
  increment: () => void;
}

const useCounterStore = create<CounterState>()(
  persist(
    (set) => ({
      count: 0,
      increment: () => set((s) => ({ count: s.count + 1 })),
    }),
    { name: 'counter-storage' }
  )
);

四、总结

Zustand 持久化的核心优势:

  1. 极简配置:一行代码实现基础持久化,无需繁琐的 reducer/action 配置;
  2. 高度灵活:支持自定义存储、加密、部分持久化、手动控制;
  3. 跨平台:适配 Web(localStorage/sessionStorage)、React Native(AsyncStorage);
  4. 性能友好:仅在状态变更时触发存储,无多余性能开销。

实际开发中,优先使用基础配置,敏感数据加解密,多场景适配自定义存储即可满足 99% 的需求。

相关推荐
vivo互联网技术7 小时前
数字人动画云端渲染方案
前端·ffmpeg·puppeteer·web3d
go_caipu7 小时前
Vben Admin管理系统集成qiankun微服务
前端·vue.js
over6977 小时前
掌控 JavaScript 的 this:从迷失到精准控制
前端·javascript·面试
天才熊猫君7 小时前
基于 `component` 的弹窗组件统一管理方案
前端·javascript
yangpow27 小时前
React 列表里 ID 转名称?一个组件搞定批量请求 + 缓存 + 懒加载
react.js
南囝coding7 小时前
《独立开发者精选工具》第 024 期
前端·后端
苏打水com7 小时前
第二十篇:Day58-60 前端性能优化进阶——从“能用”到“好用”(对标职场“体验优化”需求)
前端·css·vue·html·js
沛沛老爹7 小时前
Web开发者快速上手AI Agent:基于提示工程的旅游攻略系统实战
前端·人工智能·ai·agent·react·旅游攻略
qq_172805597 小时前
Modbus Server数据采集Web之Server端模拟功能
前端·后端·golang·modbus