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% 的需求。

相关推荐
king王一帅2 小时前
Incremark Solid 版本上线:Vue/React/Svelte/Solid 四大框架,统一体验
前端·javascript·人工智能
智航GIS7 小时前
10.4 Selenium:Web 自动化测试框架
前端·python·selenium·测试工具
前端工作日常7 小时前
我学习到的A2UI概念
前端
徐同保7 小时前
为什么修改 .gitignore 后还能提交
前端
一只小bit7 小时前
Qt 常用控件详解:按钮类 / 显示类 / 输入类属性、信号与实战示例
前端·c++·qt·gui
Mr -老鬼8 小时前
前端静态路由与动态路由:全维度总结与实践指南
前端
颜酱8 小时前
前端必备动态规划的10道经典题目
前端·后端·算法
wen__xvn9 小时前
代码随想录算法训练营DAY10第五章 栈与队列part01
java·前端·算法
大怪v10 小时前
前端佬们!!AI大势已来,未来的上限取决你的独特气质!恭请批阅!!
前端·程序员·ai编程
Mr -老鬼10 小时前
功能需求对前后端技术选型的横向建议
开发语言·前端·后端·前端框架