5月28日 01:18

Zustand 与 Redux 相比有哪些优缺点?

Zustand 的核心优势

极简 API,告别样板代码

Zustand 创建 store 只需一个函数调用,无需定义 action types、reducers、action creators。与 Redux Toolkit 的 createSlice 相比,代码量减少 60% 以上:

js
// Zustand — 一个函数搞定 store import { create } from 'zustand' const useStore = create((set) => ({ count: 0, increment: () => set((state) => ({ count: state.count + 1 })), decrement: () => set((state) => ({ count: state.count - 1 })), })) // 组件中直接用 hook function Counter() { const { count, increment } = useStore() return <button onClick={increment}>{count}</button> }
js
// Redux Toolkit — 需要更多概念 import { createSlice, configureStore } from '@reduxjs/toolkit' import { Provider, useSelector, useDispatch } from 'react-redux' const counterSlice = createSlice({ name: 'counter', initialState: { value: 0 }, reducers: { increment: (state) => { state.value += 1 }, decrement: (state) => { state.value -= 1 }, }, }) const store = configureStore({ reducer: { counter: counterSlice.reducer } }) // 还需要 Provider 包裹 + useSelector + useDispatch function Counter() { const count = useSelector((s) => s.counter.value) const dispatch = useDispatch() return <button onClick={() => dispatch(increment())}>{count}</button> }

无需 Provider,更干净的组件树

Redux 必须在应用顶层包裹 <Provider store={store}>,导致组件树多出一层嵌套,测试时也需要用 <Provider> 包裹。Zustand 直接在模块作用域创建 store,组件通过 hook 消费状态,无需任何包裹组件。这对存量项目集成尤其友好——不用改已有的组件树结构就能接入状态管理。

更小的体积,更快的加载

Gzipped 大小
Zustand~1.1 KB
Redux Toolkit~8.2 KB
Redux Toolkit + React-Redux~11.8 KB

在 3G 网络下,11 KB 的差距可带来 50-100ms 的交互时间优化。对包体积有严格限制的移动端 H5 场景,Zustand 优势明显。

内置选择器,精准控制重渲染

Zustand 支持细粒度订阅,只订阅需要的状态切片,自动跳过无关更新:

js
// 只订阅 count,其他状态变化不会触发重渲染 const count = useStore((state) => state.count) // 也支持 shallow 比较对象 import { shallow } from 'zustand/shallow' const { count, name } = useStore( (state) => ({ count: state.count, name: state.name }), shallow )

Redux 的 useSelector 同样支持选择器,但默认使用 === 严格比较,返回对象时需要手动使用 shallowEqual,容易遗忘导致不必要的重渲染。

TypeScript 开箱即用

Zustand 的 store 定义自带类型推导,无需额外声明类型:

ts
const useStore = create<{ count: number; increment: () => void }>((set) => ({ count: 0, increment: () => set((s) => ({ count: s.count + 1 })), })) // count 和 increment 类型自动推导,无需泛型

Redux Toolkit 虽然也有良好的 TS 支持,但 createSlice 和 configureStore 需要更多类型标注和泛型配置。

异步操作更直观

Zustand 处理异步不需要额外中间件,直接写 async 函数:

js
const useStore = create((set) => ({ data: null, loading: false, fetchData: async () => { set({ loading: true }) const res = await fetch('/api/data') const data = await res.json() set({ data, loading: false }) }, }))

Redux Toolkit 通过 createAsyncThunk 处理异步,需要定义 pending/fulfilled/rejected 三种状态,样板代码更多。

Zustand 的不足

生态和中间件相对薄弱

Redux 拥有 Redux Saga、Redux Observable、Redux Persist 等成熟中间件生态,处理竞态、取消、重试等复杂副作用有成熟方案。Zustand 自带 persist、devtools、immer 等中间件,覆盖常见需求,但社区中间件数量和成熟度仍远不及 Redux。遇到复杂副作用场景时,往往需要自己实现或组合多个中间件。

调试体验有差距

Zustand 可通过 devtools 中间件接入 Redux DevTools,但时间旅行调试和 action 回放功能不如原生 Redux 完善。对于需要严格追踪每次状态变更、回溯 bug 的场景,Redux 的调试体验更可靠。

大型项目实践尚在积累

Zustand 已被 React Three Fiber、shadcn/ui、Next.js 示例等项目广泛采用,2026 年 npm 周下载量达 700 万次,但在超大型企业级应用中的最佳实践仍不如 Redux 成熟。缺少千人团队的治理模式参考和大规模重构案例。

灵活性的双刃剑

Zustand 不强制状态更新模式,不同开发者可能写出风格迥异的 store。在缺乏 Code Review 约束的团队中,灵活反而变成混乱。Redux 的严格单向数据流天然约束了风格统一性,新人接手代码时理解成本更低。

什么时候选 Zustand

  • 中小型项目:状态逻辑不复杂,追求开发速度和简洁性
  • 存量项目集成:不想引入 Provider 改动组件树,零侵入接入
  • 性能敏感场景:对包体积和重渲染有严格要求,如移动端 H5
  • 快速原型开发:重视迭代速度而非架构完备性
  • React Three Fiber / 可视化项目:Zustand 是 R3F 生态的默认选择

什么时候选 Redux

  • 大型企业级应用:团队 5 人以上,需要标准化流程和严格约束
  • 复杂副作用:需要 Saga 级别的竞态处理、取消和重试能力
  • 重度调试需求:时间旅行调试对排查线上问题至关重要
  • 团队已熟悉 Redux:迁移成本高于收益,不必为了换而换
  • 金融 / 交易类系统:需要追踪每一次状态变更的审计场景

怎么选:一句结论

项目小、求快、求轻选 Zustand;项目大、求稳、求规范选 Redux。两者并非互斥——复杂项目中可以在全局状态用 Redux、局部状态用 Zustand,按需搭配。

标签:ReactReduxZustand