【前端】【React】【Zustand】[特殊字符] Zustand 系统学习大纲(实战版)
📚 Zustand 系统学习大纲(实战版)
1️⃣ 基础知识
1.1 安装
npm install zustand
使用场景
- 任何 React 项目中使用轻量级全局状态管理
优化建议
- 不需要单独安装中间件,所有插件均内置
1.2 创建 store
import create from 'zustand'interface CounterState {count: numberincrement: () => void
}export const useCounterStore = create<CounterState>((set) => ({count: 0,increment: () => set((state) => ({ count: state.count + 1 }))
}))
使用场景
- 管理单个模块状态,如计数器、表单状态
优化建议
- 拆分 store 模块,避免单 store 过大
- 使用 TS 类型约束,避免动态访问属性报错
1.3 声明属性
interface StoreState {name: stringitems: string[]loading: boolean
}
使用场景
- 存储基本类型、数组、对象、异步状态
优化建议
- 对象/数组修改使用 setter 或
immer
避免引用丢失 - 异步状态建议添加
loading
/error
字段
1.4 声明方法
setName: (newName: string) => set({ name: newName })
fetchItems: async () => {set({ loading: true })try {const res = await fetch('/api/items')const data = await res.json()set({ items: data })} catch (e) {console.error(e)} finally {set({ loading: false })}
}
使用场景
- 同步修改字段
- 异步请求更新状态
优化建议
- 异步方法需处理
loading
/error
- 避免直接修改对象引用
2️⃣ 页面中使用 store
2.1 单字段订阅
const count = useCounterStore(state => state.count)
使用场景
- 页面只依赖单个字段
优化建议 - 精准订阅字段,减少组件重渲染
2.2 解构整个 store
const { count, increment } = useCounterStore()
使用场景
- 页面需要同时使用多个字段和方法
优化建议 - 小心对象引用变化导致整组件刷新
2.3 批量订阅
const [count, items] = useCounterStore(state => [state.count, state.items])
使用场景
- 需要同时获取多个字段
优化建议 - 只订阅必要字段
- 对象/数组变化会触发刷新
2.4 非组件中获取
useCounterStore.getState().increment()
const currentCount = useCounterStore.getState().count
使用场景
- 在 utils 或事件回调中访问 store
优化建议 - 避免绕过 React 响应式
2.5 订阅变化
useCounterStore.subscribe(state => state.count,newCount => console.log('count updated', newCount)
)
使用场景
- 需要监听某字段变化进行副作用
优化建议 - 精准订阅单字段,避免全局订阅
3️⃣ 插件系统(Middleware)
3.1 DevTools
import { devtools } from 'zustand/middleware'const useStore = create(devtools((set) => ({count: 0,increment: () => set(state => ({ count: state.count + 1 }))
}), { name: 'counter' }))
使用场景
- 调试状态变化,查看 action 历史
优化建议
- 为每个 action 自定义名称
- 仅在开发环境启用 DevTools
3.2 Persist
import { persist } from 'zustand/middleware'const useStore = create(persist((set) => ({ count: 0, increment: () => set(state => ({ count: state.count + 1 })) }),{ name: 'counter-storage', getStorage: () => localStorage })
)
使用场景
- 页面刷新后仍需保持状态
- 用户数据、表单输入缓存
优化建议
- 使用
partialize
选择性持久化 - 避免大对象直接写入 localStorage
3.3 Immer
import { immer } from 'zustand/middleware/immer'const useStore = create(immer((set) => ({items: [] as string[],addItem: (item: string) => set(state => { state.items.push(item) })}))
)
使用场景
- 对对象/数组直接修改而不破坏响应式
优化建议
- 避免频繁创建新对象,提升性能
3.4 多中间件组合
import { devtools, persist, immer } from 'zustand/middleware'const useStore = create(devtools(persist(immer((set) => ({count: 0,increment: () => set(state => { state.count += 1 })})),{ name: 'counter-storage' }))
)
使用场景
- 同时需要 DevTools、持久化和 Immer
优化建议
- 中间件顺序重要:immer → persist → devtools
- 避免重复包装
4️⃣ 优化与升级
4.1 派生状态
const doubleCount = useCounterStore(state => state.count * 2)
使用场景
- 页面展示需要派生数据
优化建议 - 不存 store,避免冗余
- 可用
useMemo
缓存计算
4.2 精准订阅
const itemCount = useStore(state => state.items.length)
使用场景
- 对象/数组变化避免全量刷新
优化建议 - 对数组/对象修改使用 Immer 或新对象替换
4.3 多 store 组合
- 拆模块,按业务功能管理状态
- 页面按需引入
优化建议
- 避免单 store 过大
- 多 store 更易扩展和维护
4.4 TypeScript 类型约束
interface CarbonData { total: number; categories: string[] }
interface StoreState { carbonData: CarbonData; setCarbonData: (data: CarbonData) => void }
使用场景
- 确保状态与方法类型安全
优化建议
- 明确所有字段和方法类型,避免运行时错误
4.5 持久化选择性字段
partialize: (state) => ({ carbonData: state.carbonData })
使用场景
- 只存储必要数据
优化建议 - 减少 localStorage 占用
- 可结合 redux-devtools 调试
4.6 异步状态管理
- 管理 loading/error
- 缓存 API 数据
- 避免直接在组件中 fetch
优化建议
- 使用 store 封装 API 请求
- 返回类型统一,方便组件使用