什么是 Zustand,它与其他状态管理库相比有哪些优势?
核心答案
Zustand 是一个极简的 React 状态管理库,gzip 后仅约 1KB。它通过 create 函数创建 store,组件用 hook 订阅状态,无需 Provider 包裹,也不需要 reducer/action 等样板代码。与 Redux 相比,Zustand 的 API 更简洁、包体积小 5-7 倍、重渲染速度快 30-50%,已成为 2026 年新 React 项目的首选状态管理方案。
基本用法
创建一个 store 只需要调用 create:
jsimport { create } from 'zustand' const useStore = create((set) => ({ count: 0, increment: () => set((state) => ({ count: state.count + 1 })), decrement: () => set((state) => ({ count: state.count - 1 })), }))
组件中使用:
jsxfunction Counter() { const { count, increment, decrement } = useStore() return ( <div> <span>{count}</span> <button onClick={increment}>+1</button> <button onClick={decrement}>-1</button> </div> ) }
没有任何 Provider、没有 action 类型定义、没有 switch-case reducer。状态更新直接通过 set 函数完成。
选择性订阅:减少不必要的重渲染
这是 Zustand 的关键优势之一。组件可以只订阅它关心的状态切片:
jsx// 只在 count 变化时重渲染,其他状态更新不会触发 const count = useStore((state) => state.count) // 也可以用 selector 订阅派生状态 const isPositive = useStore((state) => state.count > 0)
相比之下,React Context 的消费者在 context 值变化时会全部重渲染,这在大型应用中是性能瓶颈。
与其他状态管理库的对比
| 特性 | Zustand | Redux Toolkit | Jotai | Valtio |
|---|---|---|---|---|
| 包体积 (gzip) | ~1KB | ~6-8KB | ~3KB | ~3KB |
| 状态模型 | 不可变 | 不可变 | 原子化 | 可变 |
| 需要 Provider | 否 | 是 | 是 | 否 |
| 样板代码量 | 极少 | 中等 | 极少 | 极少 |
| 学习曲线 | 平缓 | 中等 | 平缓 | 平缓 |
| DevTools 支持 | 内置 | 完整 | 基础 | 基础 |
| TypeScript 支持 | 优秀 | 优秀 | 优秀 | 良好 |
选择建议:中小型项目、追求简单高效选 Zustand;大型团队需要严格架构和可预测数据流选 Redux Toolkit;需要细粒度原子状态选 Jotai;偏好可变状态写法选 Valtio。
中间件:持久化与开发调试
Zustand 的中间件机制让功能扩展非常方便:
jsimport { create } from 'zustand' import { persist, devtools } from 'zustand/middleware' const useStore = create( devtools( persist( (set) => ({ theme: 'light', toggleTheme: () => set((state) => ({ theme: state.theme === 'light' ? 'dark' : 'light', })), }), { name: 'theme-storage' } ), { name: 'ThemeStore' } ) )
persist 将状态自动同步到 localStorage,页面刷新后状态不丢失。devtools 集成 Redux DevTools 扩展,方便调试状态变化。中间件可以自由组合,按需叠加。
在 Next.js 中使用
Zustand 与 Next.js App Router 配合时,需要注意 store 不能在模块顶层直接创建(服务端会共享状态),推荐使用懒初始化模式:
jsx// store.js import { create } from 'zustand' const useStore = create((set) => ({ user: null, setUser: (user) => set({ user }), })) export default useStore // layout.jsx 或 page.jsx 'use client' import useStore from './store' export default function Page() { const user = useStore((state) => state.user) return <div>{user?.name ?? '未登录'}</div> }
关键点是将使用 store 的组件标记为 'use client',store 本身保持普通的模块导出即可。
异步操作
Zustand 处理异步不需要额外的中间件(Redux 需要 redux-thunk 或 createAsyncThunk),直接在 set 中写 async 函数:
jsconst useStore = create((set) => ({ data: null, loading: false, error: null, fetchData: async (id) => { set({ loading: true, error: null }) try { const res = await fetch(`/api/data/${id}`) const data = await res.json() set({ data, loading: false }) } catch (error) { set({ error: error.message, loading: false }) } }, }))
追问:什么时候不该用 Zustand?
如果项目只需要组件间传递少量状态,React 自带的 useState + useContext 就够了,没必要引入外部库。另外,服务端状态(API 请求缓存、分页数据)更适合用 TanStack Query 管理,Zustand 专注的是客户端 UI 状态。两者经常在同一项目中配合使用——TanStack Query 管接口数据,Zustand 管界面交互状态。