🧠 Zustand 学习(对比 Pinia)

193 阅读5分钟

🧠 Zustand 学习(对比 Pinia)

🚀 作者原创 · 更新时间:2025-10-06
Zustand 是一个轻量、灵活、无模板的 React 状态管理库,是 Redux 的现代替代方案,也是 React 生态中最接近 Pinia 的状态管理工具。


📚 目录

  1. Zustand 简介

  2. 安装与初始化

  3. 核心概念

  4. 高级用法

  5. Zustand + TypeScript

  6. Zustand 与其他状态管理库对比

  7. Zustand 在 Next.js 中的最佳实践

  8. 总结


🧩 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 / RNVue / uniapp
响应模型订阅 (subscribe)Vue reactive
模块化多 store 独立模块 store
持久化persist 中间件插件
异步操作async/awaitaction
TS 支持优秀优秀
DevTools✅ Redux DevTools✅ Vue DevTools
学习成本极低
性能极快良好

✅ 结论:Zustand 是 React 世界的 “Pinia 等价物”,更灵活、更自由。


Zustand vs Redux / Recoil / Jotai

特性ZustandRedux ToolkitRecoilJotai
哲学极简函数式全局单一状态树原子化依赖图原子状态
状态存储Hook-based store全局 storeAtom / SelectorAtom
使用复杂度🔹最低🔸中等🔹中等🔹低
代码模板无模板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 的现代极简替代方案。
简洁、灵活、高性能,适合从小型到中大型项目的状态管理。