Skip to content

Zustand 是一个轻量、无样板代码(boilerplate)的 React 状态管理库。相比 Redux 更简单,相比 Context 更高效,非常适合中小型到中大型项目。

一、Zustand 核心理念

✅ 设计哲学

  • 极简 API
  • 基于 Hooks
  • 无 Provider(默认)
  • 支持中间件
  • 支持 TS 友好

核心思想:

用一个全局 store + hook 选择性订阅状态

二、基础入门

1️⃣ 安装

bash
npm install zustand

2️⃣ 创建第一个 Store

ts
import { create } from 'zustand'

const useCounterStore = create((set) => ({
  count: 0,
  increase: () => set((state) => ({ count: state.count + 1 })),
  decrease: () => set((state) => ({ count: state.count - 1 })),
}))

3️⃣ 组件中使用

tsx
function Counter() {
  const { count, increase, decrease } = useCounterStore()

  return (
    <>
      <div>{count}</div>
      <button onClick={increase}>+</button>
      <button onClick={decrease}>-</button>
    </>
  )
}
  • ✔ 自动订阅
  • ✔ 组件卸载自动取消订阅
  • ✔ 无 Provider

三、进阶核心技巧

🎯 1. 精准订阅(避免无效渲染)

ts
const count = useCounterStore((state) => state.count)

只监听 count,其他字段变化不会触发重渲染。

搭配浅比较

ts
import { shallow } from 'zustand/shallow'

const { count, increase } = useCounterStore(
  (state) => ({ count: state.count, increase: state.increase }),
  shallow
)

🎯 2. 异步操作(请求接口)

ts
const useUserStore = create((set) => ({
  user: null,
  loading: false,
  fetchUser: async () => {
    set({ loading: true })
    const res = await fetch('/api/user')
    const data = await res.json()
    set({ user: data, loading: false })
  }
}))
  • ✔ Zustand 不限制 async
  • ✔ 无 thunk / saga 概念

🎯 3. 中间件使用

persist(本地存储)

ts
import { persist } from 'zustand/middleware'

const useStore = create(
  persist(
    (set) => ({
      theme: 'light',
      toggle: () => set((s) => ({ theme: s.theme === 'light' ? 'dark' : 'light' })),
    }),
    {
      name: 'app-storage',
    }
  )
)

devtools(调试)

ts
import { devtools } from 'zustand/middleware'

const useStore = create(
  devtools((set) => ({
    count: 0,
    inc: () => set((s) => ({ count: s.count + 1 }), false, 'inc')
  }))
)

可在 Redux DevTools 中查看。

🎯 4. 拆分模块(大型项目)

推荐结构:

store/
 ├── index.ts
 ├── user.ts
 ├── cart.ts
 └── theme.ts

模块合并

ts
import { create } from 'zustand'
import { createUserSlice } from './user'
import { createCartSlice } from './cart'

export const useStore = create((...a) => ({
  ...createUserSlice(...a),
  ...createCartSlice(...a),
}))
  • ✔ 类似 Redux slice
  • ✔ 更清晰
  • ✔ 可维护性强

四、高级实战技巧

🔥 1. get() 读取最新状态

ts
const useStore = create((set, get) => ({
  count: 0,
  double: () => {
    const current = get().count
    set({ count: current * 2 })
  }
}))

🔥 2. subscribe 监听(非组件环境)

ts
const unsubscribe = useStore.subscribe(
  (state) => state.count,
  (count) => console.log('count changed:', count)
)

适用于:

  • 日志系统
  • websocket
  • 外部模块监听

🔥 3. immer 中间件(复杂对象)

ts
import { immer } from 'zustand/middleware/immer'

const useStore = create(
  immer((set) => ({
    user: { name: '', age: 0 },
    updateName: (name: string) =>
      set((state) => {
        state.user.name = name
      }),
  }))
)
  • ✔ 可变写法
  • ✔ 内部自动 immutable

🔥 4. 重置 Store

ts
const initialState = { count: 0 }

const useStore = create((set) => ({
  ...initialState,
  reset: () => set(initialState),
}))

五、性能优化总结

✅ 必做

  • 使用 selector
  • 使用 shallow
  • 拆分 store
  • 避免大对象直接返回

❌ 避免

  • 整个 state 解构
  • 在 store 里写复杂 UI 状态
  • 所有状态放一个 store

六、Zustand vs Redux 对比

对比ZustandRedux
学习成本
模板代码
中间件支持
适合项目中小/中大型中大型

七、企业级最佳实践

1️⃣ 状态分层

  • UI状态(本地)
  • 全局共享状态(Zustand)
  • 服务器状态(React Query)

推荐组合:

  • Zustand + React Query
  • Zustand + Vite
  • Zustand + Next.js

2️⃣ 不要滥用全局状态

判断标准:

  • 多组件共享?
  • 是否跨页面?
  • 是否需要缓存?

否则使用 useState。

八、常见坑总结

  • ⚠ StrictMode 下初始化两次
  • ⚠ persist 需要处理版本升级
  • ⚠ SSR 需避免状态污染
  • ⚠ 异步并发注意覆盖问题

九、实战项目结构示例

src/
 ├── store/
 │   ├── authStore.ts
 │   ├── productStore.ts
 │   └── index.ts
 ├── hooks/
 ├── pages/
 └── services/

🔟 终极总结

Zustand 适合:

  • ✔ 追求简单
  • ✔ 不想写 reducer
  • ✔ 不想配置复杂中间件
  • ✔ 希望极致开发体验

一句话总结:

小而美,够用且强大。