从 Redux 的繁琐到 Zustand 的优雅:探索 2025 年最受欢迎的 React 状态管理方案
目录
- 为什么选择 Zustand
- 核心原理与架构
- 源码深度解析
- 实战应用模式
- 中间件系统
- 性能优化
- 最佳实践
为什么选择 Zustand
状态管理的演进
在 React 生态中,状态管理经历了从 Redux 的"样板代码地狱"到现代化轻量方案的转变。2025 年,Zustand 以其简洁性和强大功能成为开发者的首选,GitHub 上获得 5.5万+ stars ,周下载量达到 1276万+ 。
Zustand vs 传统方案
| 特性 | Redux | Context API | Zustand | Jotai |
|---|---|---|---|---|
| 样板代码 | 多 | 少 | 极少 | 少 |
| 学习曲线 | 陡峭 | 平缓 | 平缓 | 中等 |
| 性能 | 优秀 | 一般 | 优秀 | 优秀 |
| Bundle 大小 | ~15KB | 0 (内置) | ~1.2KB | ~3KB |
| 中间件支持 | 强大 | 无 | 灵活 | 有限 |
| DevTools | 完善 | 无 | 支持 | 支持 |
| TypeScript | 优秀 | 良好 | 优秀 | 优秀 |
Zustand 的核心优势:
- 极简 API:无需 Provider、Reducer、Action Types
- 高性能 :基于
useSyncExternalStore,避免 Context 的性能陷阱 - 灵活架构:支持多 Store 模式,更符合模块化开发
- 强大中间件:内置 persist、devtools、immer 等中间件
- TypeScript 友好:完善的类型推断,无需手写类型
适用场景
根据 2025 年社区实践,Zustand 最适合:
- 中大型应用:需要全局状态但不想引入 Redux 复杂性
- 快速迭代项目:减少样板代码,提升开发效率
- 性能敏感场景:细粒度订阅,避免不必要的重渲染
- 团队协作:代码简洁,降低新人学习成本
核心原理与架构
设计哲学
Zustand(德语"状态")的设计遵循三大原则:
- 单向数据流:State → View → Action → State
- 不可变更新 :通过
set函数创建新状态 - 选择器订阅:组件只订阅需要的状态切片
核心架构
scss
┌─────────────────────────────────────────┐
│ Zustand Store │
├─────────────────────────────────────────┤
│ Vanilla Core (vanilla.ts) │
│ ├─ createStore() │
│ │ ├─ State Storage │
│ │ ├─ Listeners Set │
│ │ └─ setState/getState/subscribe │
│ │ │
│ React Binding (react.ts) │
│ └─ create() │
│ └─ useSyncExternalStore() │
│ │
│ Middleware Layer │
│ ├─ persist (localStorage/sessionStorage)│
│ ├─ devtools (Redux DevTools) │
│ ├─ immer (不可变更新) │
│ └─ subscribeWithSelector │
└─────────────────────────────────────────┘
关键技术:useSyncExternalStore
Zustand 基于 React 18 的 useSyncExternalStore Hook,这是 React 官方提供的订阅外部状态的标准方案:
TypeScript
// React 18 的核心 Hook
useSyncExternalStore(
subscribe, // 订阅函数
getSnapshot, // 获取当前状态
getServerSnapshot // SSR 时的服务端状态
)
优势:
- 并发模式兼容:支持 React 18 的并发特性
- 避免撕裂:保证状态一致性(tearing-free)
- 性能优化:只在订阅的状态变化时触发重渲染
源码深度解析
1. Vanilla 核心实现(~43 行代码)
Zustand 的核心极其精简,让我们逐步解析:
TypeScript
// zustand/vanilla.ts (简化版)
export const createStore = (createState) => {
let state // 状态存储
const listeners = new Set() // 订阅者集合
// 设置状态
const setState = (partial, replace) => {
const nextState = replace
? partial
: Object.assign({}, state, partial)
if (!Object.is(nextState, state)) {
const previousState = state
state = nextState
listeners.forEach((listener) => listener(state, previousState))
}
}
// 获取状态
const getState = () => state
// 订阅状态变化
const subscribe = (listener) => {
listeners.add(listener)
return () => listeners.delete(listener) // 返回取消订阅函数
}
// 销毁 Store
const destroy = () => listeners.clear()
const api = { setState, getState, subscribe, destroy }
state = createState(setState, getState, api)
return api
}
核心机制:
- 闭包存储状态 :
state变量通过闭包保存在内存中 - 发布-订阅模式 :
listenersSet 管理所有订阅者 - 浅比较优化 :
Object.is()避免无意义的更新 - 不可变更新 :
Object.assign()创建新对象
2. React 绑定层
TypeScript
// zustand/react.ts (简化版)
import { useSyncExternalStore } from 'react'
import { createStore } from './vanilla'
export const create = (createState) => {
const api = createStore(createState)
const useBoundStore = (selector = api.getState, equalityFn = Object.is) => {
// 核心:使用 React 18 的 useSyncExternalStore
const slice = useSyncExternalStore(
api.subscribe, // 订阅函数
() => selector(api.getState()), // 获取选中的状态切片
() => selector(api.getState()) // SSR 时的状态
)
return slice
}
Object.assign(useBoundStore, api)
return useBoundStore
}
关键点:
- 选择器模式 :
selector函数提取需要的状态切片 - 相等性检查 :
equalityFn决定是否触发重渲染(默认Object.is) - API 继承 :
useBoundStore同时拥有 Hook 和 Store API
3. 中间件实现原理
Zustand 的中间件本质是高阶函数 ,包装 createState:
TypeScript
// 中间件类型定义
type StateCreator<t> = (
set: SetState<t>,
get: GetState<t>,
api: StoreApi<t>
) => T
type Middleware<t> = (
createState: StateCreator<t>
) => StateCreator<t>
// 示例:日志中间件
const logger = (createState) => (set, get, api) => {
const loggedSet = (args) => {
console.log('Previous State:', get())
set(args)
console.log('New State:', get())
}
return createState(loggedSet, get, api)
}
// 使用
const useStore = create(logger((set) => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 }))
})))
中间件链式调用:
TypeScript
// 多个中间件组合
const useStore = create(
devtools(
persist(
immer((set) => ({
user: { name: '', age: 0 },
updateName: (name) => set((state) => {
state.user.name = name // immer 支持直接修改
})
})),
{ name: 'user-store' } // persist 配置
)
)
)
4. Persist 中间件源码解析
TypeScript
// zustand/middleware/persist.ts (核心逻辑)
export const persist = (config, options) => (set, get, api) => {
const { name, storage = localStorage } = options
// 从存储中恢复状态
const hydrate = () => {
const storedValue = storage.getItem(name)
if (storedValue) {
const parsedValue = JSON.parse(storedValue)
set(parsedValue, true) // replace 模式
}
}
// 订阅状态变化,自动保存
api.subscribe((state) => {
storage.setItem(name, JSON.stringify(state))
})
// 初始化时恢复
hydrate()
return config(set, get, api)
}
持久化流程:
- 初始化 :从
localStorage读取并恢复状态 - 自动保存:订阅状态变化,每次更新自动写入存储
- 序列化 :使用
JSON.stringify/parse(可自定义)
实战应用模式
1. 基础 Store 创建
TypeScript
// stores/counterStore.ts
import { create } from 'zustand'
interface CounterState {
count: number
increment: () => void
decrement: () => void
reset: () => void
}
export const useCounterStore = create<counterstate>((set) => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 })),
decrement: () => set((state) => ({ count: state.count - 1 })),
reset: () => set({ count: 0 })
}))
// 组件中使用
function Counter() {
const count = useCounterStore((state) => state.count)
const increment = useCounterStore((state) => state.increment)
return (
<div>
<p>Count: {count}</p>
<button onclick="{increment}">+1</button>
</div>
)
}
</counterstate>
2. 异步操作处理
TypeScript
// stores/userStore.ts
import { create } from 'zustand'
interface User {
id: string
name: string
email: string
}
interface UserState {
user: User | null
loading: boolean
error: string | null
fetchUser: (id: string) => Promise<void>
updateUser: (data: Partial<user>) => Promise<void>
}
export const useUserStore = create<userstate>((set, get) => ({
user: null,
loading: false,
error: null,
fetchUser: async (id) => {
set({ loading: true, error: null })
try {
const response = await fetch(`/api/users/${id}`)
const user = await response.json()
set({ user, loading: false })
} catch (error) {
set({ error: error.message, loading: false })
}
},
updateUser: async (data) => {
const { user } = get()
if (!user) return
set({ loading: true })
try {
const response = await fetch(`/api/users/${user.id}`, {
method: 'PATCH',
body: JSON.stringify(data)
})
const updatedUser = await response.json()
set({ user: updatedUser, loading: false })
} catch (error) {
set({ error: error.message, loading: false })
}
}
}))
3. 模块化 Store(推荐大型项目)
TypeScript
typescript
// stores/slices/authSlice.ts
export interface AuthSlice {
token: string | null
isAuthenticated: boolean
login: (token: string) => void
logout: () => void
}
export const createAuthSlice = (set): AuthSlice => ({
token: null,
isAuthenticated: false,
login: (token) => set({ token, isAuthenticated: true }),
logout: () => set({ token: null, isAuthenticated: false })
})
// stores/slices/profileSlice.ts
export interface ProfileSlice {
profile: User | null
updateProfile: (data: Partial<user>) => void
}
export const createProfileSlice = (set): ProfileSlice => ({
profile: null,
updateProfile: (data) => set((state) => ({
profile: { ...state.profile, ...data }
}))
})
// stores/index.ts - 组合 Store
import { create } from 'zustand'
import { createAuthSlice, AuthSlice } from './slices/authSlice'
import { createProfileSlice, ProfileSlice } from './slices/profileSlice'
type StoreState = AuthSlice & ProfileSlice
export const useStore = create<storestate>()((...a) => ({
...createAuthSlice(...a),
...createProfileSlice(...a)
}))
4. 计算属性(Computed Values)
TypeScript
// stores/cartStore.ts
import { create } from 'zustand'
interface CartItem {
id: string
name: string
price: number
quantity: number
}
interface CartState {
items: CartItem[]
addItem: (item: CartItem) => void
removeItem: (id: string) => void
// 计算属性通过 selector 实现
}
export const useCartStore = create<cartstate>((set) => ({
items: [],
addItem: (item) => set((state) => ({
items: [...state.items, item]
})),
removeItem: (id) => set((state) => ({
items: state.items.filter(item => item.id !== id)
}))
}))
// 计算属性作为独立 selector
export const selectTotalPrice = (state: CartState) =>
state.items.reduce((sum, item) => sum + item.price * item.quantity, 0)
export const selectItemCount = (state: CartState) =>
state.items.reduce((sum, item) => sum + item.quantity, 0)
// 组件中使用
function CartSummary() {
const totalPrice = useCartStore(selectTotalPrice)
const itemCount = useCartStore(selectItemCount)
return (
<div>
<p>Items: {itemCount}</p>
<p>Total: ${totalPrice.toFixed(2)}</p>
</div>
)
}
5. Next.js App Router 集成(SSR)
TypeScript
javascript
// stores/themeStore.ts
import { create } from 'zustand'
import { persist, createJSONStorage } from 'zustand/middleware'
interface ThemeState {
theme: 'light' | 'dark'
toggleTheme: () => void
}
export const useThemeStore = create<themestate>()(
persist(
(set) => ({
theme: 'light',
toggleTheme: () => set((state) => ({
theme: state.theme === 'light' ? 'dark' : 'light'
}))
}),
{
name: 'theme-storage',
storage: createJSONStorage(() => localStorage)
}
)
)
// app/providers.tsx - 客户端 Provider
'use client'
import { useEffect, useState } from 'react'
import { useThemeStore } from '@/stores/themeStore'
export function Providers({ children }) {
const [mounted, setMounted] = useState(false)
useEffect(() => {
setMounted(true)
}, [])
if (!mounted) {
return null // 避免 SSR 水合不匹配
}
return <>{children}
}
中间件系统
1. 内置中间件
Persist - 状态持久化
TypeScript
import { create } from 'zustand'
import { persist } from 'zustand/middleware'
const useStore = create(
persist(
(set) => ({
user: null,
setUser: (user) => set({ user })
}),
{
name: 'user-storage', // localStorage key
storage: createJSONStorage(() => localStorage),
partialize: (state) => ({ user: state.user }), // 部分持久化
onRehydrateStorage: () => (state) => {
console.log('Hydration finished', state)
}
}
)
)
Devtools - Redux DevTools 集成
TypeScript
javascript
import { devtools } from 'zustand/middleware'
const useStore = create(
devtools(
(set) => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 }), false, 'increment')
}),
{ name: 'CounterStore' }
)
)
Immer - 不可变更新简化
TypeScript
import { immer } from 'zustand/middleware/immer'
const useStore = create(
immer((set) => ({
user: { name: 'John', address: { city: 'NYC' } },
updateCity: (city) => set((state) => {
state.user.address.city = city // 直接修改,immer 处理不可变性
})
}))
)
2. 自定义中间件
日志中间件
TypeScript
const logger = (config) => (set, get, api) =>
config(
(...args) => {
console.log(' applying', args)
set(...args)
console.log(' new state', get())
},
get,
api
)
const useStore = create(logger((set) => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 }))
})))
时间旅行中间件
TypeScript
const timeTravel = (config) => (set, get, api) => {
const history = []
const maxHistory = 10
return {
...config(
(...args) => {
history.push(get())
if (history.length > maxHistory) history.shift()
set(...args)
},
get,
api
),
undo: () => {
if (history.length > 0) {
const previous = history.pop()
set(previous, true)
}
}
}
}
请求去重中间件
TypeScript
const dedupe = (config) => (set, get, api) => {
const pending = new Map()
return config(
(partial, replace, action) => {
if (typeof action === 'string' && pending.has(action)) {
return pending.get(action)
}
const promise = Promise.resolve(set(partial, replace, action))
if (typeof action === 'string') {
pending.set(action, promise)
promise.finally(() => pending.delete(action))
}
return promise
},
get,
api
)
}
性能优化
1. 选择器优化
TypeScript
// ❌ 不好:每次渲染都创建新对象
function BadComponent() {
const data = useStore((state) => ({
user: state.user,
posts: state.posts
}))
// 即使 user 和 posts 没变,对象引用变了,导致重渲染
}
// ✅ 好:使用 shallow 比较
import { shallow } from 'zustand/shallow'
function GoodComponent() {
const { user, posts } = useStore(
(state) => ({ user: state.user, posts: state.posts }),
shallow
)
}
// ✅ 更好:分别订阅
function BetterComponent() {
const user = useStore((state) => state.user)
const posts = useStore((state) => state.posts)
}
2. 避免不必要的重渲染
TypeScript
// ❌ 不好:订阅整个 Store
function BadList() {
const store = useStore() // 任何状态变化都会重渲染
return <div>{store.items.map(...)}</div>
}
// ✅ 好:只订阅需要的部分
function GoodList() {
const items = useStore((state) => state.items)
return <div>{items.map(...)}</div>
}
3. 使用 subscribeWithSelector
TypeScript
import { subscribeWithSelector } from 'zustand/middleware'
const useStore = create(
subscribeWithSelector((set) => ({
user: { name: 'John', age: 30 },
posts: []
}))
)
// 只在 user.name 变化时触发
useStore.subscribe(
(state) => state.user.name,
(name, prevName) => {
console.log('Name changed:', prevName, '->', name)
}
)
4. 大列表优化
TypeScript
// stores/todoStore.ts
import { create } from 'zustand'
const useTodoStore = create((set) => ({
todos: [],
addTodo: (todo) => set((state) => ({
todos: [...state.todos, todo]
})),
toggleTodo: (id) => set((state) => ({
todos: state.todos.map(todo =>
todo.id === id ? { ...todo, completed: !todo.completed } : todo
)
}))
}))
// 使用 React.memo 和选择器
const TodoItem = React.memo(({ id }) => {
const todo = useTodoStore((state) =>
state.todos.find(t => t.id === id)
)
const toggleTodo = useTodoStore((state) => state.toggleTodo)
return (
<div onclick="{()" ==""> toggleTodo(id)}>
{todo.text}
</div>
)
})
function TodoList() {
const todoIds = useTodoStore((state) => state.todos.map(t => t.id))
return todoIds.map(id => <todoitem key="{id}" id="{id}">)
}
最佳实践
1. Store 组织结构
bash
src/
├── stores/
│ ├── index.ts # 导出所有 Store
│ ├── slices/ # Store 切片
│ │ ├── authSlice.ts
│ │ ├── userSlice.ts
│ │ └── cartSlice.ts
│ ├── middleware/ # 自定义中间件
│ │ ├── logger.ts
│ │ └── analytics.ts
│ └── selectors/ # 复用的选择器
│ ├── cartSelectors.ts
│ └── userSelectors.ts
2. TypeScript 类型安全
TypeScript
// stores/types.ts
export interface User {
id: string
name: string
email: string
}
export interface AuthState {
user: User | null
token: string | null
isAuthenticated: boolean
}
export interface AuthActions {
login: (email: string, password: string) => Promise<void>
logout: () => void
refreshToken: () => Promise<void>
}
// stores/authStore.ts
import { create } from 'zustand'
import type { AuthState, AuthActions } from './types'
type AuthStore = AuthState & AuthActions
export const useAuthStore = create<authstore>((set, get) => ({
// State
user: null,
token: null,
isAuthenticated: false,
// Actions
login: async (email, password) => {
const response = await fetch('/api/login', {
method: 'POST',
body: JSON.stringify({ email, password })
})
const { user, token } = await response.json()
set({ user, token, isAuthenticated: true })
},
logout: () => {
set({ user: null, token: null, isAuthenticated: false })
},
refreshToken: async () => {
const { token } = get()
if (!token) return
const response = await fetch('/api/refresh', {
headers: { Authorization: `Bearer ${token}` }
})
const { token: newToken } = await response.json()
set({ token: newToken })
}
}))
3. 测试策略
TypeScript
// stores/__tests__/counterStore.test.ts
import { renderHook, act } from '@testing-library/react'
import { useCounterStore } from '../counterStore'
describe('CounterStore', () => {
beforeEach(() => {
useCounterStore.setState({ count: 0 })
})
it('should increment count', () => {
const { result } = renderHook(() => useCounterStore())
act(() => {
result.current.increment()
})
expect(result.current.count).toBe(1)
})
it('should reset count', () => {
const { result } = renderHook(() => useCounterStore())
act(() => {
result.current.increment()
result.current.increment()
result.current.reset()
})
expect(result.current.count).toBe(0)
})
})
4. 错误处理模式
TypeScript
interface AsyncState<t> {
data: T | null
loading: boolean
error: Error | null
}
const createAsyncSlice = <t>(
fetcher: () => Promise<t>
) => ({
...initialAsyncState,
fetch: async (set) => {
set({ loading: true, error: null })
try {
const data = await fetcher()
set({ data, loading: false })
} catch (error) {
set({ error, loading: false })
}
}
})
5. 性能监控
TypeScript
const performanceMiddleware = (config) => (set, get, api) => {
return config(
(...args) => {
const start = performance.now()
set(...args)
const end = performance.now()
if (end - start > 16) { // 超过一帧时间
console.warn(`Slow state update: ${end - start}ms`)
}
},
get,
api
)
}
6. 开发环境调试
TypeScript
// stores/devtools.ts
import { devtools } from 'zustand/middleware'
export const withDevtools = <t>(
config: StateCreator<t>,
name: string
) => {
if (process.env.NODE_ENV === 'development') {
return devtools(config, { name })
}
return config
}
// 使用
const useStore = create(
withDevtools(
(set) => ({ count: 0, increment: () => set((s) => ({ count: s.count + 1 })) }),
'CounterStore'
)
)
总结
Zustand 的核心价值
- 极简设计:43 行核心代码,1.2KB 体积
- 高性能 :基于
useSyncExternalStore,细粒度订阅 - 灵活扩展:强大的中间件系统
- 开发体验:TypeScript 友好,DevTools 支持
何时选择 Zustand
- ✅ 中小型到大型应用
- ✅ 需要全局状态但不想用 Redux
- ✅ 性能敏感场景
- ✅ 快速迭代项目
何时不选择 Zustand
- ❌ 超大型企业应用(考虑 Redux + RTK)
- ❌ 需要严格的状态管理规范(Redux 更合适)
- ❌ 原子化状态需求(考虑 Jotai/Recoil)
学习资源
作者注:本文基于 Zustand v5.0 和 2025 年社区最佳实践编写,涵盖了从原理到实战的完整知识体系。希望能帮助你深入理解并高效使用 Zustand!
最后更新:2025年10月