Redux 和 Zustand 对比及二者可序列化相关问题

时之世 发布于 2025-09-08 397 次阅读 预计阅读时间: 6 分钟 最后更新于 19 天前 1339 字 无~


Redux 和 Zustand 的核心差异之一在于对 「可序列化数据」 的要求,尤其是 Redux 明确不建议存储函数,而 Zustand 更加灵活

下面我们从 设计理念、可序列化性、函数存储、适用场景 等多个维度进行系统对比,并重点解释 「可序列化」 和 「为什么 Redux 不该存函数」 。


一、核心对比概览

特性ReduxZustand
设计理念单一可信源 + 严格的不可变性 + 可预测性轻量、简单、灵活、函数式
状态存储Store 是纯对象 (推荐可序列化)Store 可以是任意类型 (包括函数)
是否支持存储函数不推荐 (违反可序列化)支持
中间件生态丰富 (Redux Thunk, Saga, DevTools)简单中间件支持
调试工具Redux DevTools(时间旅行调试)Zustand DevTools(类似)
学习成本较高 (action, reducer, store, middleware)极低 (一个 create 函数)
包体积较大 (需配合 toolkit)极小 (~1.5KB)

二、什么是 「可序列化」(Serializable)?

可序列化数据:

指可以被 JSON.stringify() 正确转换的数据类型,主要包括:

//  可序列化
{
  number: 42,
  string: "hello",
  boolean: true,
  null: null,
  array: [1, 2, 3],
  object: { a: 1 },
  undefined → 会被忽略
}

不可序列化数据:

无法被 JSON.stringify() 正确处理:

{
  function: () => {},        //  函数
  symbol: Symbol('id'),       // Symbol
  date: new Date(),           //  会被转成字符串
  regex: /abc/,               //  会被转成空对象
  undefined: undefined,       //  会被忽略
  bigint: 123n,               //  需特殊处理
  nan: NaN,                   // 会变成 null
  infinity: Infinity          //  会变成 null
}

三、为什么 Redux 要求状态可序列化?为什么不能存函数?

这是 Redux 核心设计哲学决定的,主要原因有:

1. 支持时间旅行调试 (Time Travel Debugging)

  • Redux DevTools 可以记录每一步 action,并让你 「回退」 到任意状态。
  • 如果状态中包含函数,回退后函数的闭包环境可能已改变,导致行为不一致。
// 错误示例:状态中存函数
const store = createStore(() => ({
  actions: {
    increment: () => dispatch({ type: 'INC' })
  }
}))

回退后,increment 函数引用的 dispatch 可能已失效。

2. 服务端渲染 (SSR) 和状态 hydration

  • SSR 时,服务器将状态序列化为 JSON 字符串,发送给客户端。
  • 客户端再反序列化 (JSON.parse()) 恢复状态。
  • 函数无法被序列化,会丢失。
// 服务器
res.send(`<script>window.__INITIAL_STATE__ = ${JSON.stringify(store.getState())}</script>`);

// 客户端
const preloadedState = window.__INITIAL_STATE__; // 函数字段会丢失!

3. 持久化 (如 localStorage)

  • 使用 redux-persist 将状态存入 localStorage
  • localStorage 只支持字符串,必须 JSON.stringify()
  • 函数无法存储。

4. 可预测性和可测试性

  • Redux 希望状态是 「纯数据」,reducer 是纯函数。
  • 函数是 「行为」,混入状态会破坏数据与逻辑的分离。

四、 Redux 中如何处理 「函数逻辑」?

虽然不能把函数存进 state,但 Redux 提供了其他方式:

1. Action Creators(函数)

export const increment = () => ({ type: 'INCREMENT' });

2. Redux Thunk / Redux Saga

用于处理异步和副作用:

// Thunk
const fetchUser = (id) => async (dispatch, getState) => {
  const res = await api.getUser(id);
  dispatch({ type: 'USER_RECEIVED', payload: res.data });
};

3. Selector(用 Reselect 缓存计算)

import { createSelector } from 'reselect';

const selectItems = state => state.items;
const selectTax = state => state.tax;

export const selectTotal = createSelector(
  [selectItems, selectTax],
  (items, tax) => items.reduce(...) * (1 + tax)
);

总结:逻辑放外面,状态只存数据。


五、 Zustand 为什么可以存储函数?

Zustand 的设计更现代、更灵活,它不强制要求状态可序列化

1. Zustand 允许在 store 中直接定义函数

import { create } from 'zustand';

const useStore = create((set, get) => ({
  count: 0,
  // 直接定义函数
  increment: () => set((state) => ({ count: state.count + 1 })),
  decrement: () => set((state) => ({ count: state.count - 1 })),
  // 甚至可以存外部函数
  logger: console.log,
}));

2. 使用方式更简洁

const { count, increment } = useStore();
<button onClick={increment}>+1</button>

3. 也支持 actions 分离 (推荐)

const useStore = create((set) => ({
  count: 0,
  setCount: (count) => set({ count }),
}));

// 使用
const { count, setCount } = useStore();

六、 Zustand 的序列化问题

虽然 Zustand 允许存函数,但如果你需要:

  • 持久化到 localStorage
  • SSR hydration
  • 时间旅行调试

你就需要手动处理函数的序列化问题。

解决方案:使用中间件 persist

import { create } from 'zustand';
import { persist } from 'zustand/middleware';

const useStore = create(persist(
  (set, get) => ({
    count: 0,
    name: 'John',
    //  函数不会被持久化
    increment: () => set({ count: get().count + 1 }),
  }),
  {
    name: 'my-store', // 存储名
    // 自定义序列化 (可选)
    serialize: (state) => JSON.stringify({ state: state.state }),
    deserialize: (str) => ({ state: JSON.parse(str).state })
  }
));

persist 中间件会自动忽略函数,只持久化可序列化数据。


七、如何选择?Redux vs Zustand

场景推荐
大型复杂应用,需要严格状态管理、时间旅行、 SSRRedux + RTK
中小型项目,追求开发效率、轻量Zustand
需要持久化、 SSRRedux 更成熟,Zustand 需配置
团队协作,规范性强Redux(约束多,不易出错)
快速原型开发Zustand(几行代码搞定)

总结

问题回答
Redux 为什么不能存函数?为了支持可序列化:时间旅行、 SSR 、持久化、可预测性
什么是可序列化?能被 JSON.stringify 正确处理的数据 (纯数据)
Zustand 为什么能存函数?设计更灵活,不强制序列化,适合现代轻量应用
Zustand 能持久化吗?可以,但函数不会被保存,需用 persist 中间件
我该用哪个?复杂项目用 Redux,简单项目用 Zustand