1. 引言为什么选择 Zustand在 React 应用开发中状态管理是一个绕不开的话题。从早期的 Redux 到后来的 MobX、Recoil开发者们一直在寻找一个既强大又简洁的解决方案。Zustand德语意为“状态”正是在这样的背景下诞生它以其极简的 API、出色的 TypeScript 支持以及近乎零样板代码的特性迅速成为 React 社区的新宠。本文将带你从零开始系统性地学习 Zustand。你将了解到Zustand 的核心设计哲学与优势如何创建、使用和订阅一个 Store与 React 深度集成的各种模式处理异步操作、持久化、中间件等高级用法通过完整的实战 Demo 巩固所学知识无论你是 React 新手还是正在为现有项目的状态管理方案寻找更优解这篇文章都将为你提供清晰的路径和实用的代码。2. 环境准备与项目初始化在开始编写代码之前我们需要一个 React 开发环境。你可以使用 Vite 或 Create React App 快速搭建。2.1 创建 React 项目以 Vite 为例打开终端执行以下命令# 使用 npmnpmcreate vitelatest my-zustand-app ----templatereact-ts# 或使用 yarnyarncreate vite my-zustand-app--templatereact-ts# 进入项目目录cdmy-zustand-app2.2 安装 Zustand在项目根目录下安装 Zustand 依赖npminstallzustand# 或yarnaddzustand现在你的项目已经准备好了。接下来让我们深入 Zustand 的核心概念。3. Zustand 核心概念解析3.1 Store状态的单一来源在 Zustand 中Store是所有状态和操作逻辑的容器。你可以把它想象成一个自定义的 Hook但它可以在组件外独立存在和更新。创建一个 Store 的核心是create函数它接受一个回调函数该函数返回一个状态对象。3.2 State 与 Actions状态与变更Zustand 鼓励你将状态State和改变状态的方法Actions放在同一个 Store 中。这种模式类似于 Vuex 的mutationsactions但更加直观和一体化。State普通的 JavaScript 对象存储应用数据。ActionsStore 中定义的函数用于更新 State。它们可以直接通过set函数修改状态。3.3 Selector精准订阅与性能优化Zustand 的一个巨大优势是选择性订阅。组件可以只订阅 Store 中它关心的那一小部分状态而不是整个 Store。当其他不相关的状态变化时该组件不会重新渲染这极大地提升了性能。4. 第一个 Zustand Store计数器示例让我们从一个经典的计数器开始创建你的第一个 Store。4.1 创建 Store 文件在src目录下创建stores/counterStore.tsimport{create}fromzustand;// 定义 Store 的状态类型interfaceCounterState{count:number;// Actionsincrement:()void;decrement:()void;reset:()void;incrementByAmount:(amount:number)void;}// 使用 create 函数创建 StoreexportconstuseCounterStorecreateCounterState((set)({// 初始状态count:0,// Actions通过 set 函数更新状态increment:()set((state)({count:state.count1})),decrement:()set((state)({count:state.count-1})),reset:()set({count:0}),incrementByAmount:(amount)set((state)({count:state.countamount})),}));4.2 在 React 组件中使用创建一个组件src/components/Counter.tsximport React from react; import { useCounterStore } from ../stores/counterStore; export const Counter: React.FC () { // 使用 Hook 获取 Store 中的状态和 Actions const { count, increment, decrement, reset, incrementByAmount } useCounterStore(); return ( div style{{ padding: 20px, border: 1px solid #ccc, borderRadius: 8px }} h2计数器 Demo/h2 p当前计数: strong{count}/strong/p div style{{ display: flex, gap: 10px, marginTop: 15px }} button onClick{decrement}-/button button onClick{increment}/button button onClick{() incrementByAmount(5)}5/button button onClick{reset}重置/button /div /div ); };4.3 在主应用中使用修改src/App.tsximport React from react; import ./App.css; import { Counter } from ./components/Counter; function App() { return ( div classNameApp h1Zustand 从零学习 Demo/h1 Counter / /div ); } export default App;运行npm run dev你将看到一个可以交互的计数器。恭喜你已经成功创建并使用了第一个 Zustand Store5. 进阶用法与模式5.1 使用 Selector 避免不必要的重渲染在大型应用中直接解构整个 Store 可能导致性能问题。我们应该使用 Selector 来精确订阅。// 低效订阅了整个 Store任何状态变化都会导致重渲染 const { count, userName } useCounterStore(); // 高效使用 Selector只订阅 count 状态 const count useCounterStore((state) state.count); const increment useCounterStore((state) state.increment); // 只有当 count 变化时这个组件才会重渲染5.2 处理异步 ActionZustand 处理异步操作非常自然你可以在 Action 中直接使用async/await。interfaceUserState{users:User[];loading:boolean;error:string|null;fetchUsers:()Promisevoid;}exportconstuseUserStorecreateUserState((set)({users:[],loading:false,error:null,fetchUsers:async(){set({loading:true,error:null});try{constresponseawaitfetch(https://api.example.com/users);constusersawaitresponse.json();set({users,loading:false});}catch(error){set({error:获取用户失败,loading:false});}},}));5.3 状态持久化Persist MiddlewareZustand 提供了中间件系统。使用persist中间件可以轻松将状态保存到localStorage或sessionStorage。首先安装中间件包npminstallzustand/middleware# 注意zustand/middleware 是内置的无需额外安装 persist 包使用示例import{create}fromzustand;import{persist}fromzustand/middleware;interfaceSettingsState{theme:light|dark;toggleTheme:()void;}exportconstuseSettingsStorecreateSettingsState()(persist((set)({theme:light,toggleTheme:()set((state)({theme:state.themelight?dark:dark})),}),{name:settings-storage,// localStorage 中的 key// 可选只持久化部分状态// partialize: (state) ({ theme: state.theme }),}));5.4 在组件外使用 Store有时你需要在 React 组件之外例如在工具函数、事件监听器中读取或修改状态。Zustand Store 本身是一个独立对象可以直接调用其方法。// 在非组件环境中import{useCounterStore}from./stores/counterStore;// 获取当前状态不会触发订阅constcurrentCountuseCounterStore.getState().count;// 执行 ActionuseCounterStore.getState().increment();// 监听状态变化手动管理订阅constunsubscribeuseCounterStore.subscribe((state)state.count,(count){console.log(Count changed to:,count);});// 记得在适当的时候取消订阅// unsubscribe();6. 综合实战Todo 应用让我们构建一个更完整的 Todo 应用来巩固所有概念。6.1 定义 Todo Store创建src/stores/todoStore.tsimport{create}fromzustand;exportinterfaceTodo{id:string;text:string;completed:boolean;}interfaceTodoState{todos:Todo[];filter:all|active|completed;addTodo:(text:string)void;toggleTodo:(id:string)void;deleteTodo:(id:string)void;setFilter:(filter:all|active|completed)void;getFilteredTodos:()Todo[];}exportconstuseTodoStorecreateTodoState((set,get)({todos:[],filter:all,addTodo:(text)set((state)({todos:[...state.todos,{id:Date.now().toString(),text,completed:false}],})),toggleTodo:(id)set((state)({todos:state.todos.map((todo)todo.idid?{...todo,completed:!todo.completed}:todo),})),deleteTodo:(id)set((state)({todos:state.todos.filter((todo)todo.id!id),})),setFilter:(filter)set({filter}),// 使用 get() 访问当前状态计算派生状态getFilteredTodos:(){const{todos,filter}get();switch(filter){caseactive:returntodos.filter((todo)!todo.completed);casecompleted:returntodos.filter((todo)todo.completed);default:returntodos;}},}));6.2 创建 Todo 组件创建src/components/TodoApp.tsximport React, { useState } from react; import { useTodoStore } from ../stores/todoStore; import ./TodoApp.css; // 可选的样式文件 export const TodoApp: React.FC () { const [inputText, setInputText] useState(); const { addTodo, toggleTodo, deleteTodo, setFilter, filter, getFilteredTodos } useTodoStore(); const todos useTodoStore(getFilteredTodos); // 使用 Selector 订阅派生状态 const handleSubmit (e: React.FormEvent) { e.preventDefault(); if (inputText.trim()) { addTodo(inputText.trim()); setInputText(); } }; return ( div classNametodo-container h2Zustand Todo List/h2 form onSubmit{handleSubmit} input typetext value{inputText} onChange{(e) setInputText(e.target.value)} placeholder输入新的待办事项... / button typesubmit添加/button /form div classNamefilter-buttons {([all, active, completed] as const).map((f) ( button key{f} className{filter f ? active : } onClick{() setFilter(f)} {f all ? 全部 : f active ? 未完成 : 已完成} /button ))} /div ul classNametodo-list {todos.map((todo) ( li key{todo.id} className{todo.completed ? completed : } input typecheckbox checked{todo.completed} onChange{() toggleTodo(todo.id)} / span{todo.text}/span button onClick{() deleteTodo(todo.id)}删除/button /li ))} /ul p总计: {todos.length} 项/p /div ); };6.3 更新主应用更新src/App.tsx集成 Todo 应用import React from react; import ./App.css; import { Counter } from ./components/Counter; import { TodoApp } from ./components/TodoApp; function App() { return ( div classNameApp h1Zustand 从零学习 Demo/h1 div style{{ display: grid, gridTemplateColumns: 1fr 1fr, gap: 30px, marginTop: 30px }} Counter / TodoApp / /div footer style{{ marginTop: 40px, fontSize: 0.9em, color: #666 }} p这个 Demo 展示了 Zustand 的核心功能创建 Store、定义 Actions、使用 Selector、处理派生状态。/p /footer /div ); } export default App;7. 总结与最佳实践7.1 Zustand 的优势总结极简 APIcreate和useStore几乎就是全部学习成本极低。出色的 TypeScript 支持类型推断完善开发体验流畅。高性能默认支持选择性订阅自动避免不必要的重渲染。中间件生态通过官方和社区中间件轻松实现持久化、日志、Immer 集成等功能。框架无关性虽然与 React 集成最好但其核心 Store 可以在任何 JavaScript 环境中使用。7.2 推荐的项目结构src/ ├── stores/ │ ├── index.ts // 统一导出所有 Store │ ├── counterStore.ts │ ├── todoStore.ts │ └── userStore.ts ├── components/ │ ├── Counter.tsx │ └── TodoApp.tsx └── App.tsx7.3 下一步学习建议官方文档Zustand GitHub 是学习的第一站文档简洁但涵盖所有细节。中间件探索尝试zustand/middleware中的devtoolsRedux DevTools 集成、immer不可变更新等。状态分片对于超大型应用可以考虑按业务域创建多个 Store而非一个巨型 Store。与 React Query/SWR 结合Zustand 管理客户端状态React Query 管理服务器状态二者是绝配。8. 常见问题解答FAQQ: Zustand 和 Redux Toolkit 比优势在哪A: Zustand 的代码量更少不需要定义slices、reducers、configureStore概念更简单。对于大多数中小型应用Zustand 是更轻量、更快速的选择。Q: 如何在 Class 组件中使用 ZustandA: 虽然 Zustand 主要面向函数组件但你可以通过 HOC 或 Context 将其注入 Class 组件或者直接使用store.getState()和store.subscribe。Q: 状态持久化有大小限制吗A: 有。localStorage通常有 5-10MB 的限制。对于大量数据考虑使用indexedDB或服务端存储并配合persist中间件的自定义存储引擎。Q: Zustand 适合大型企业级应用吗A: 是的。其选择性订阅和组合 Store 的能力足以支撑大型应用。许多知名公司包括 GitHub 上的大型开源项目已将其用于生产环境。希望这篇从零开始的指南能帮助你顺利上手 Zustand。动手实践文中的 Demo你将会深刻体会到其简洁与强大。Happy coding