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

相关推荐
layman052810 分钟前
webpack5 css-loader:从基础到原理
前端·css·webpack
半桔11 分钟前
【前端小站】CSS 样式美学:从基础语法到界面精筑的实战宝典
前端·css·html
AI老李12 分钟前
PostCSS完全指南:功能/配置/插件/SourceMap/AST/插件开发/自定义语法
前端·javascript·postcss
_OP_CHEN14 分钟前
【前端开发之CSS】(一)初识 CSS:网页化妆术的终极指南,新手也能轻松拿捏页面美化!
前端·css·html·网页开发·样式表·界面美化
啊哈一半醒16 分钟前
CSS 主流布局
前端·css·css布局·标准流 浮动 定位·flex grid 响应式布局
PHP武器库18 分钟前
ULUI:不止于按钮和菜单,一个专注于“业务组件”的纯 CSS 框架
前端·css
电商API_1800790524727 分钟前
第三方淘宝商品详情 API 全维度调用指南:从技术对接到生产落地
java·大数据·前端·数据库·人工智能·网络爬虫
晓晓莺歌28 分钟前
vue3某一个路由切换,导致所有路由页面均变成空白页
前端·vue.js
Up九五小庞1 小时前
开源埋点分析平台 ClkLog 本地部署 + Web JS 埋点测试实战--九五小庞
前端·javascript·开源
摘星编程1 小时前
React Native + OpenHarmony:UniversalLink通用链接
javascript·react native·react.js