中间件系统 #
什么是中间件? #
中间件是一种在状态更新前后插入自定义逻辑的机制。Zustand 的中间件系统非常灵活,可以用于日志记录、状态持久化、不可变数据处理等场景。
中间件结构 #
tsx
type Middleware = (
config: StateCreator
) => (set: SetState, get: GetState, api: StoreApi) => State
// 简化理解
const myMiddleware = (config) => (set, get, api) => {
// 自定义逻辑
return config(set, get, api)
}
使用中间件 #
基本用法 #
tsx
import { create } from 'zustand'
import { devtools, persist } from 'zustand/middleware'
const useStore = create(
devtools(
persist(
(set) => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 })),
}),
{ name: 'my-store' }
)
)
)
中间件执行顺序 #
text
请求 → devtools → persist → immer → 原始 store
响应 ← devtools ← persist ← immer ← 原始 store
内置中间件 #
devtools - 开发工具 #
与 Redux DevTools 集成,方便调试:
tsx
import { devtools } from 'zustand/middleware'
const useStore = create(
devtools(
(set) => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 })),
decrement: () => set((state) => ({ count: state.count - 1 })),
}),
{ name: 'CounterStore' }
)
)
devtools 配置选项 #
tsx
devtools(
(set) => ({ /* store */ }),
{
name: 'MyStore', // Store 名称
enabled: true, // 是否启用
anonymousActionType: '匿名操作', // 匿名 action 名称
store: 'my-store', // Redux DevTools 中的 store 名称
}
)
为 Action 命名 #
tsx
const useStore = create(
devtools(
(set) => ({
count: 0,
// 使用第三个参数命名 action
increment: () => set(
(state) => ({ count: state.count + 1 }),
false,
'increment'
),
// 或使用对象形式
add: (amount: number) => set(
(state) => ({ count: state.count + amount }),
false,
{ type: 'add', amount }
),
}),
{ name: 'CounterStore' }
)
)
persist - 状态持久化 #
将状态保存到本地存储:
tsx
import { persist, createJSONStorage } from 'zustand/middleware'
interface State {
count: number
increment: () => void
}
const useStore = create<State>()(
persist(
(set) => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 })),
}),
{
name: 'counter-storage', // 存储名称
}
)
)
persist 配置选项 #
tsx
persist(
(set) => ({ /* store */ }),
{
name: 'my-storage', // 存储名称(必需)
storage: createJSONStorage(() => sessionStorage), // 存储方式
partialize: (state) => ({ // 只持久化部分状态
count: state.count,
}),
onRehydrateStorage: () => (state) => {
console.log('状态已恢复', state)
},
version: 1, // 版本号
migrate: (persisted, version) => {
// 迁移逻辑
if (version === 0) {
return { ...persisted, newField: 'default' }
}
return persisted
},
merge: (persisted, current) => ({
...current,
...persisted,
}),
}
)
选择存储引擎 #
tsx
// localStorage(默认)
persist(store, {
name: 'storage',
storage: createJSONStorage(() => localStorage),
})
// sessionStorage
persist(store, {
name: 'storage',
storage: createJSONStorage(() => sessionStorage),
})
// 自定义存储
persist(store, {
name: 'storage',
storage: {
getItem: (name) => {
const value = customStorage.get(name)
return value ?? null
},
setItem: (name, value) => {
customStorage.set(name, value)
},
removeItem: (name) => {
customStorage.remove(name)
},
},
})
immer - 不可变数据处理 #
简化复杂状态的更新:
tsx
import { immer } from 'zustand/middleware/immer'
interface State {
user: {
name: string
settings: {
theme: 'light' | 'dark'
notifications: boolean
}
}
updateUser: (name: string) => void
updateTheme: (theme: 'light' | 'dark') => void
}
const useStore = create<State>()(
immer((set) => ({
user: {
name: 'John',
settings: {
theme: 'light',
notifications: true,
},
},
// 直接"修改"状态
updateUser: (name) => set((state) => {
state.user.name = name
}),
updateTheme: (theme) => set((state) => {
state.user.settings.theme = theme
}),
}))
)
redux - Redux 兼容 #
使用 Redux 风格的 reducer:
tsx
import { redux } from 'zustand/middleware'
type State = { count: number }
type Action = { type: 'INCREMENT' } | { type: 'DECREMENT' }
const reducer = (state: State, action: Action): State => {
switch (action.type) {
case 'INCREMENT':
return { count: state.count + 1 }
case 'DECREMENT':
return { count: state.count - 1 }
default:
return state
}
}
const useStore = create(
redux(reducer, { count: 0 })
)
// 使用
function Component() {
const { count, dispatch } = useStore()
return (
<div>
<p>{count}</p>
<button onClick={() => dispatch({ type: 'INCREMENT' })}>+</button>
<button onClick={() => dispatch({ type: 'DECREMENT' })}>-</button>
</div>
)
}
combine - 类型推断增强 #
改进 TypeScript 类型推断:
tsx
import { combine } from 'zustand/middleware'
const useStore = create(
combine({ count: 0 }, (set) => ({
increment: () => set((state) => ({ count: state.count + 1 })),
decrement: () => set((state) => ({ count: state.count - 1 })),
}))
)
subscribeWithSelector - 高级订阅 #
支持选择器订阅:
tsx
import { subscribeWithSelector } from 'zustand/middleware'
const useStore = create(
subscribeWithSelector((set) => ({
count: 0,
name: 'test',
increment: () => set((state) => ({ count: state.count + 1 })),
}))
)
// 在组件外订阅特定状态变化
useStore.subscribe(
(state) => state.count,
(count) => {
console.log('Count changed:', count)
}
)
自定义中间件 #
日志中间件 #
tsx
const logger = (config) => (set, get, api) =>
config(
(args) => {
console.log('旧状态:', get())
set(args)
console.log('新状态:', get())
},
get,
api
)
const useStore = create(
logger((set) => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 })),
}))
)
时间戳中间件 #
tsx
const timestamp = (config) => (set, get, api) =>
config(
(args) => {
set({
...args,
updatedAt: Date.now(),
})
},
get,
api
)
const useStore = create(
timestamp((set) => ({
count: 0,
updatedAt: null,
increment: () => set((state) => ({ count: state.count + 1 })),
}))
)
验证中间件 #
tsx
const validator = (schema) => (config) => (set, get, api) =>
config(
(args) => {
const newState = typeof args === 'function' ? args(get()) : args
// 验证新状态
const result = schema.safeParse(newState)
if (!result.success) {
console.error('验证失败:', result.error)
return
}
set(args)
},
get,
api
)
// 使用 Zod 验证
import { z } from 'zod'
const schema = z.object({
count: z.number().min(0).max(100),
name: z.string(),
})
const useStore = create(
validator(schema)((set) => ({
count: 0,
name: '',
increment: () => set((state) => ({ count: state.count + 1 })),
}))
)
节流中间件 #
tsx
const throttle = (ms: number) => (config) => (set, get, api) => {
let lastCall = 0
return config(
(args) => {
const now = Date.now()
if (now - lastCall >= ms) {
lastCall = now
set(args)
}
},
get,
api
)
}
const useStore = create(
throttle(1000)((set) => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 })),
}))
)
重置中间件 #
tsx
const reset = (initialState) => (config) => (set, get, api) =>
config(
(args) => set(args),
get,
{
...api,
reset: () => set(initialState),
}
)
const initialState = { count: 0, name: '' }
const useStore = create(
reset(initialState)((set) => ({
...initialState,
increment: () => set((state) => ({ count: state.count + 1 })),
}))
)
// 使用
useStore.getState().reset()
组合中间件 #
多个中间件组合 #
tsx
import { create } from 'zustand'
import { devtools, persist } from 'zustand/middleware'
import { immer } from 'zustand/middleware/immer'
const useStore = create(
devtools(
persist(
immer((set) => ({
user: {
name: '',
settings: {
theme: 'light' as 'light' | 'dark',
},
},
updateName: (name: string) => set((state) => {
state.user.name = name
}),
updateTheme: (theme: 'light' | 'dark') => set((state) => {
state.user.settings.theme = theme
}),
})),
{ name: 'user-storage' }
),
{ name: 'UserStore' }
)
)
中间件工厂函数 #
tsx
function createLoggerMiddleware(name: string) {
return (config) => (set, get, api) =>
config(
(args) => {
console.log(`[${name}] 旧状态:`, get())
set(args)
console.log(`[${name}] 新状态:`, get())
},
get,
api
)
}
const useUserStore = create(
createLoggerMiddleware('UserStore')((set) => ({
user: null,
setUser: (user) => set({ user }),
}))
)
const useCartStore = create(
createLoggerMiddleware('CartStore')((set) => ({
items: [],
addItem: (item) => set((state) => ({ items: [...state.items, item] })),
}))
)
实际案例 #
完整配置的 Store #
tsx
import { create } from 'zustand'
import { devtools, persist, createJSONStorage } from 'zustand/middleware'
import { immer } from 'zustand/middleware/immer'
interface Todo {
id: string
text: string
completed: boolean
createdAt: number
}
interface TodoState {
todos: Todo[]
filter: 'all' | 'active' | 'completed'
addTodo: (text: string) => void
toggleTodo: (id: string) => void
removeTodo: (id: string) => void
setFilter: (filter: TodoState['filter']) => void
clearCompleted: () => void
}
const useTodoStore = create<TodoState>()(
devtools(
persist(
immer((set) => ({
todos: [],
filter: 'all',
addTodo: (text) => set((state) => {
state.todos.push({
id: Date.now().toString(),
text,
completed: false,
createdAt: Date.now(),
})
}),
toggleTodo: (id) => set((state) => {
const todo = state.todos.find((t) => t.id === id)
if (todo) {
todo.completed = !todo.completed
}
}),
removeTodo: (id) => set((state) => {
const index = state.todos.findIndex((t) => t.id === id)
if (index !== -1) {
state.todos.splice(index, 1)
}
}),
setFilter: (filter) => set({ filter }),
clearCompleted: () => set((state) => {
state.todos = state.todos.filter((t) => !t.completed)
}),
})),
{
name: 'todo-storage',
storage: createJSONStorage(() => localStorage),
partialize: (state) => ({
todos: state.todos,
filter: state.filter,
}),
version: 1,
migrate: (persisted: any, version) => {
if (version === 0) {
return {
...persisted,
todos: persisted.todos.map((t: any) => ({
...t,
createdAt: Date.now(),
})),
}
}
return persisted
},
}
),
{ name: 'TodoStore' }
)
)
最佳实践 #
1. 中间件顺序 #
tsx
// ✅ 推荐顺序
create(
devtools( // 最外层:调试
persist( // 中间层:持久化
immer( // 内层:数据处理
(set) => ({ /* store */ })
)
)
)
)
2. 条件启用中间件 #
tsx
const useStore = create(
process.env.NODE_ENV === 'development'
? devtools(store, { name: 'MyStore' })
: store
)
3. 类型安全 #
tsx
// 使用类型断言确保类型安全
const useStore = create<State>()(
persist(
(set) => ({ /* store */ }),
{ name: 'storage' }
)
)
总结 #
中间件系统是 Zustand 的强大特性:
- devtools:调试支持
- persist:状态持久化
- immer:简化不可变更新
- redux:Redux 兼容
- 可以自定义中间件扩展功能
接下来,让我们深入学习 状态持久化,掌握数据本地存储技巧。
最后更新:2026-03-28