🧠 Zustand 学习(对比 Pinia)
🚀 作者原创 · 更新时间:2025-10-06
Zustand 是一个轻量、灵活、无模板的 React 状态管理库,是 Redux 的现代替代方案,也是 React 生态中最接近 Pinia 的状态管理工具。
📚 目录
🧩 Zustand 简介
Zustand 是由 pmndrs 团队 开发的 React 状态管理库(同为 react-three-fiber 作者)。
特性一览:
- ⚡ 超轻量(仅 1KB 左右)
- 🧘♂️ 无需 Provider(像 Redux 那样)
- 🔒 支持持久化、异步、中间件
- 💪 原生 TypeScript 支持
- ⚙️ 无模板语法,纯函数式定义
⚙️ 安装与初始化
# npm
npm install zustand
# yarn
yarn add zustand
# pnpm
pnpm add zustand
🏗️ 核心概念
创建 store
import { create } from 'zustand'
interface CounterState {
count: number
increase: () => void
decrease: () => void
}
export const useCounterStore = create<CounterState>((set) => ({
count: 0,
increase: () => set((s) => ({ count: s.count + 1 })),
decrease: () => set((s) => ({ count: s.count - 1 })),
}))
读取与更新 state
import { useCounterStore } from './store/counter'
export default function Counter() {
const { count, increase, decrease } = useCounterStore()
return (
<div>
<h1>{count}</h1>
<button onClick={increase}>+</button>
<button onClick={decrease}>-</button>
</div>
)
}
选择器(Selectors)
避免不必要的重渲染:
const count = useCounterStore((state) => state.count)
🧠 高级用法
状态持久化(persist)
import { create } from 'zustand'
import { persist } from 'zustand/middleware'
export const useUserStore = create(
persist(
(set) => ({
name: 'Guest',
setName: (name) => set({ name }),
}),
{ name: 'user-storage' } // localStorage key
)
)
中间件(middleware)
Zustand 支持:
persist(持久化)devtools(Redux DevTools)subscribeWithSelector(精准订阅)
import { devtools } from 'zustand/middleware'
export const useStore = create(
devtools((set) => ({
bears: 0,
add: () => set((s) => ({ bears: s.bears + 1 })),
}))
)
异步操作
export const useTodoStore = create((set) => ({
todos: [],
fetchTodos: async () => {
const res = await fetch('/api/todos')
set({ todos: await res.json() })
},
}))
多 store 管理
export const useUserStore = create(() => ({ name: 'Alice' }))
export const useThemeStore = create(() => ({ theme: 'dark' }))
🧩 Zustand + TypeScript
类型安全且自动推导:
interface BearState {
bears: number
addBear: () => void
}
export const useBearStore = create<BearState>((set) => ({
bears: 0,
addBear: () => set((s) => ({ bears: s.bears + 1 })),
}))
⚔️ Zustand 与其他状态管理库对比
Zustand vs Pinia
| 特性 | Zustand (React) | Pinia (Vue 3) |
|---|---|---|
| 创建方式 | create() | defineStore() |
| 框架支持 | React / RN | Vue / uniapp |
| 响应模型 | 订阅 (subscribe) | Vue reactive |
| 模块化 | 多 store 独立 | 模块 store |
| 持久化 | persist 中间件 | 插件 |
| 异步操作 | async/await | action |
| TS 支持 | 优秀 | 优秀 |
| DevTools | ✅ Redux DevTools | ✅ Vue DevTools |
| 学习成本 | 极低 | 低 |
| 性能 | 极快 | 良好 |
✅ 结论:Zustand 是 React 世界的 “Pinia 等价物”,更灵活、更自由。
Zustand vs Redux / Recoil / Jotai
| 特性 | Zustand | Redux Toolkit | Recoil | Jotai |
|---|---|---|---|---|
| 哲学 | 极简函数式 | 全局单一状态树 | 原子化依赖图 | 原子状态 |
| 状态存储 | Hook-based store | 全局 store | Atom / Selector | Atom |
| 使用复杂度 | 🔹最低 | 🔸中等 | 🔹中等 | 🔹低 |
| 代码模板 | 无模板 | Action + Reducer | 依赖图配置 | Atom 定义 |
| 性能 | 高(精准订阅) | 中 | 高 | 高 |
| DevTools | ✅ 支持 | ✅ Redux DevTools | ⚠️ 实验性 | ❌ 无官方 |
| 持久化 | ✅ 内置 persist | 插件实现 | 需第三方 | 自行实现 |
| 适合场景 | 中小型 / 高性能需求 | 大型项目 | 原子依赖逻辑 | 极简状态 |
| SSR 支持 | ✅ 易用 | ✅ 完整 | ✅ 支持 | ⚠️ 手动处理 |
💬 总结:
- Redux:重量级、适合大型团队。
- Recoil:适合复杂依赖。
- Jotai:最轻但原子粒度管理麻烦。
- Zustand:灵活、简单、性能优异,实用首选。
🏗️ Zustand 在 Next.js 中的最佳实践
1️⃣ 创建全局 store
📁 store/useUserStore.ts
'use client'
import { create } from 'zustand'
import { persist } from 'zustand/middleware'
interface UserState {
name: string
setName: (name: string) => void
}
export const useUserStore = create<UserState>()(
persist(
(set) => ({
name: 'Guest',
setName: (name) => set({ name }),
}),
{ name: 'user-store' } // localStorage key
)
)
2️⃣ 客户端组件中使用
📁 app/page.tsx
'use client'
import { useUserStore } from '@/store/useUserStore'
export default function Home() {
const { name, setName } = useUserStore()
return (
<div className="p-4">
<h1>Hello, {name}</h1>
<button onClick={() => setName('Feipeng')}>改名</button>
</div>
)
}
✅ Next.js 13+ 中,Zustand 必须在
"use client"组件中使用。
3️⃣ SSR 场景下使用(可选)
如果你需要在服务器端预加载状态:
import { useServerInsertedHTML } from 'next/navigation'
export function useZustandHydration(store: any) {
useServerInsertedHTML(() => {
const state = JSON.stringify(store.getState())
return <script dangerouslySetInnerHTML={{ __html: `window.__ZUSTAND__=${state}` }} />
})
}
SSR 下可结合
zustand/vanilla创建非 React 依赖的 store。
🧪 实战示例:Todo 应用
// store/todo.ts
import { create } from 'zustand'
interface Todo {
id: number
text: string
done: boolean
}
interface TodoState {
todos: Todo[]
addTodo: (text: string) => void
toggleTodo: (id: number) => void
}
export const useTodoStore = create<TodoState>((set) => ({
todos: [],
addTodo: (text) =>
set((s) => ({ todos: [...s.todos, { id: Date.now(), text, done: false }] })),
toggleTodo: (id) =>
set((s) => ({ todos: s.todos.map((t) => (t.id === id ? { ...t, done: !t.done } : t)) })),
}))
// components/TodoApp.tsx
'use client'
import { useTodoStore } from '@/store/todo'
import { useState } from 'react'
export default function TodoApp() {
const { todos, addTodo, toggleTodo } = useTodoStore()
const [text, setText] = useState('')
return (
<div className="p-4">
<h2 className="text-xl font-bold mb-2">📝 Todo List</h2>
<div className="flex gap-2">
<input
className="border p-2 rounded"
value={text}
onChange={(e) => setText(e.target.value)}
/>
<button className="bg-blue-500 text-white px-3 py-2 rounded" onClick={() => addTodo(text)}>
添加
</button>
</div>
<ul className="mt-4">
{todos.map((t) => (
<li key={t.id} onClick={() => toggleTodo(t.id)} className="cursor-pointer">
{t.done ? '✅' : '⬜'} {t.text}
</li>
))}
</ul>
</div>
)
}
🧾 总结
| 优势 | 说明 |
|---|---|
| 🚀 性能 | 精确订阅,最小渲染开销 |
| 💡 灵活 | 纯函数式 API,无模板约束 |
| 🔒 中间件 | 内置持久化、DevTools |
| 🧱 易集成 | Next.js / React Native 无缝支持 |
| 🧘 TypeScript | 自动类型推导,无痛使用 |
💬 一句话总结:
✅ Zustand 是 React 世界中的 Pinia,是 Redux 的现代极简替代方案。
简洁、灵活、高性能,适合从小型到中大型项目的状态管理。