第 4 章 MobX 状态管理入门

发布时间:2026/10/16 6:33:26
第 4 章 MobX 状态管理入门 本章目标学会用 MobX 管理应用状态这是项目的核心⭐⭐⭐ 难度进阶基础 | ⏱️ 预计时间2 小时4.1 为什么需要状态管理先想一个问题做一个 TodoList 很简单用useState就够了function TodoList() { const [todos, setTodos] useState([]); // ... }但是如果有好几个组件都要用同一份数据呢┌──────────────┐ │ 侧边栏组件 │ ← 显示未读数量 └──────┬───────┘ │ 共享同一份数据 ┌──────▼───────┐ │ 聊天窗口组件 │ ← 显示消息列表 └──────┬───────┘ │ ┌──────▼───────┐ │ 头部组件 │ ← 显示当前对话标题 └──────────────┘如果每个组件都自己存一份数据会有什么问题数据不一致一个组件更新了另一个没更重复请求每个组件都去拉一次数据很难同步A 组件改了数据B 组件怎么知道状态管理库就是来解决这个问题的数据存在一个地方所有组件都从这拿改了大家都知道。为什么选 MobX 而不是 Redux对比项ReduxMobX学习曲线陡概念多reducer/action/middleware…平缓改数据就行样板代码多少更新方式不可变每次返回新对象可变直接改适合场景大型复杂应用强约束中大型应用开发效率高MobX 的核心理念就一句话你改数据UI 自动更新。4.2 MobX 三个核心概念MobX 有三个最核心的概念掌握了它们就掌握了 MobX 的 80%┌──────────┐ ┌──────────┐ ┌──────────┐ │ observable │────▶│ reaction │────▶│ action │ │(可观察的)│ │(反应)│ │(动作)│ └──────────┘ └──────────┘ └──────────┘ 数据 UI 更新 修改数据的方法1. observable可观察的被 MobX 监控的数据。你改了它用到它的地方会自动更新。import{makeAutoObservable}frommobx;classStore{count0;// 这个 count 就是 observableconstructor(){makeAutoObservable(this);// 把这个类的所有属性都变成 observable}}2. action动作修改 observable 数据的方法。虽然 MobX 允许直接改但规范上建议用 action 方法来改。classStore{count0;constructor(){makeAutoObservable(this);}// 这个 increment 就是 actionincrement(){this.count1;}}3. reaction反应当 observable 变化时自动执行的东西。最常见的就是 React 组件重新渲染。import { observer } from mobx-react-lite; // 用 observer 包裹组件组件就会「观察」用到的 observable const Counter observer(({ store }: { store: Store }) { return ( div p计数{store.count}/p {/* 这里用到了 countcount 变了就自动重渲染 */} button onClick{() store.increment()}1/button /div ); });一句话总结数据是 observable 的改数据用 action组件用 observer 包一下就自动更新。就这么简单4.3 动手写第一个 Store安装 MobXnpminstallmobx mobx-react-lite# 或者yarnaddmobx mobx-react-lite小贴士mobx是核心库mobx-react-lite是 React 的绑定库轻量版只支持函数组件。写一个计数器 Store创建src/store/CounterStore.tsimport{makeAutoObservable}frommobx;classCounterStore{// observable可观察的数据count0;constructor(){// 魔法函数自动把所有属性变成 observable所有方法变成 actionmakeAutoObservable(this);}// action修改数据的方法increment(){this.count1;}decrement(){this.count-1;}reset(){this.count0;}// computed计算属性也是一种 observable基于其他数据计算出来的getdoubleCount(){returnthis.count*2;}}// 导出单例exportconstcounterStorenewCounterStore();在组件中使用创建src/components/Counter.tsximport { observer } from mobx-react-lite; import { counterStore } from /store/CounterStore; // 用 observer 包裹组件 const Counter observer(() { return ( div style{{ padding: 20 }} h3MobX 计数器演示/h3 p当前计数{counterStore.count}/p p翻倍后{counterStore.doubleCount}/p button onClick{() counterStore.increment()}1/button button onClick{() counterStore.decrement()}-1/button button onClick{() counterStore.reset()}重置/button /div ); }); export default Counter;试试看把这个组件加到工作台页面里点击按钮看看效果。你会发现不用useState不用setCount(...)数据改了UI 自动就更新了是不是很神奇这就是 MobX 的魔力✨4.4 computed计算属性什么是计算属性get开头的方法它的值是从其他 observable 计算出来的。classTodoStore{todos[{id:1,text:学 MobX,done:true},{id:2,text:学网关层,done:false},];// 计算属性已完成的数量getcompletedCount(){returnthis.todos.filter(tt.done).length;}// 计算属性未完成的数量getremainingCount(){returnthis.todos.filter(t!t.done).length;}// 计算属性完成率getcompletionRate(){if(this.todos.length0)return0;returnMath.round((this.completedCount/this.todos.length)*100);}}计算属性的特点自动缓存只要依赖的数据没变多次访问不会重复计算惰性求值没人用的时候不会计算也是 observable计算属性变了用到它的组件也会自动更新小贴士能用 computed 就别用普通方法。普通方法每次调用都会执行computed 会缓存结果。4.5 根 Store 子 Store 模式当项目变大了把所有数据都塞在一个 Store 里会爆炸。所以我们用根 Store 子 Store的模式RootStore根 Store ├── AgentStore# 管 Agent 列表├── ConversationStore# 管会话列表├── SessionStore# 管当前会话的消息├── ExecutionStore# 管执行面板└──...根 Store 负责创建所有子 Store协调跨子 Store 的业务逻辑作为统一的入口动手实现创建src/store/AgentStore.tsimport{makeAutoObservable}frommobx;// Agent 的类型定义exportinterfaceAgent{id:number;name:string;description:string;avatar?:string;online:boolean;}classAgentStore{agents:Agent[][];selectedAgentId:number|nullnull;loadingfalse;keyword;constructor(){makeAutoObservable(this);}// 查询方法 getselectedAgent():Agent|null{returnthis.agents.find(aa.idthis.selectedAgentId)??null;}getfilteredAgents():Agent[]{constkwthis.keyword.toLowerCase();if(!kw)returnthis.agents;returnthis.agents.filter(aa.name.toLowerCase().includes(kw)||a.description.toLowerCase().includes(kw));}// 修改方法 setAgents(agents:Agent[]){this.agentsagents;}selectAgent(id:number){this.selectedAgentIdid;}setLoading(loading:boolean){this.loadingloading;}setKeyword(keyword:string){this.keywordkeyword;}// 局部更新某个 AgentpatchAgent(id:number,patch:PartialAgent){constagentthis.agents.find(aa.idid);if(agent){Object.assign(agent,patch);}}}exportconstagentStorenewAgentStore();创建src/store/RootStore.tsimport{makeAutoObservable}frommobx;import{AgentStore}from./AgentStore;// 后面会加更多子 store...classRootStore{readonlyagentStorenewAgentStore();// readonly conversationStore new ConversationStore();// readonly sessionStore new SessionStore();// ...constructor(){// 注意根 store 本身也可以是 observable 的makeAutoObservable(this,{},{autoBind:true});}// 根 Store 可以有一些协调方法asyncbootstrap(){this.agentStore.setLoading(true);try{// 模拟加载数据awaitnewPromise(resolvesetTimeout(resolve,500));this.agentStore.setAgents([{id:1,name:小运,description:运维助手,online:true},{id:2,name:小办,description:办公助手,online:true},{id:3,name:小审,description:审核助手,online:false},]);}finally{this.agentStore.setLoading(false);}}}exportconstrootStorenewRootStore();为什么根 Store 很重要因为很多业务逻辑是跨 Store 的。比如「切换 Agent」这个动作asyncselectAgent(agentId:number){// 1. 切换选中的 AgentAgentStorethis.agentStore.selectAgent(agentId);// 2. 清空执行面板ExecutionStorethis.executionStore.reset();// 3. 加载该 Agent 的会话列表ConversationStorethis.conversationStore.setLoadingAgent(agentId);constconversationsawaitgateway.getConversations(agentId);this.conversationStore.setConversations(agentId,conversations);// 4. 打开默认会话SessionStore// ...}这些逻辑放在任何一个子 Store 里都不合适因为涉及到多个 Store 的协调所以放在根 Store 里。4.6 在 React 中注入 Store方法一直接导入单例最简单import { observer } from mobx-react-lite; import { rootStore } from /store/RootStore; const AgentList observer(() { const { agentStore } rootStore; if (agentStore.loading) return div加载中.../div; return ( div {agentStore.filteredAgents.map(agent ( div key{agent.id} onClick{() agentStore.selectAgent(agent.id)} h3{agent.name}/h3 p{agent.description}/p span{agent.online ? 在线 : 离线}/span /div ))} /div ); });优点简单直接缺点组件和 Store 强耦合不好测试方法二React Context推荐创建src/store/StoreContext.tsximport React, { createContext, useContext } from react; import { rootStore, RootStore } from ./RootStore; const StoreContext createContextRootStore(rootStore); export const StoreProvider: React.FC{ children: React.ReactNode } ({ children }) ( StoreContext.Provider value{rootStore}{children}/StoreContext.Provider ); // 自定义 Hook方便使用 export const useStore () useContext(StoreContext);在main.tsx里用 Provider 包裹import { StoreProvider } from /store/StoreContext; ReactDOM.createRoot(document.getElementById(root)!).render( StoreProvider App / /StoreProvider );在组件中使用import { observer } from mobx-react-lite; import { useStore } from /store/StoreContext; const AgentList observer(() { const { agentStore } useStore(); // ... });优点解耦便于测试支持多实例缺点稍微多写一点代码小贴士小项目用单例就够了中大型项目推荐用 Context。我们这个教程后面用 Context 的方式。4.7 MobX 最佳实践✅ 好的做法用makeAutoObservable最省事自动识别组件用observer包裹只有被 observer 包裹的组件才会响应式更新数据尽量扁平化嵌套太深的对象容易出问题用 computed 派生数据不要自己手动维护派生状态细粒度拆分 Store一个 Store 管一件事❌ 不好的做法直接修改 observable 的深层属性虽然能工作但不清晰所有东西都塞一个 Store 里后期会变成屎山在组件里直接改 observable应该通过 action 方法改滥用 reaction / autorun大部分场景用 computed observer 就够了4.8 本章小结你学到了什么✅ 理解了为什么需要状态管理✅ 掌握了 MobX 三个核心概念observable / action / reaction✅ 学会了写第一个 Store✅ 理解了 computed 计算属性✅ 学会了根 Store 子 Store 的组织方式✅ 知道了怎么在 React 组件中使用 Store核心概念回顾概念作用怎么用observable可观察的数据类的属性 makeAutoObservableaction修改数据的方法类的方法 makeAutoObservablecomputed计算属性get xxx()observer让组件响应式更新observer(组件)根 Store协调多个子 Store把子 Store 作为属性组合起来✏️ 动手练习跟着本章的例子把 CounterStore 和 AgentStore 写出来跑通新增一个TodoStore包含 todo 列表、添加 todo、切换完成状态、清空已完成在页面中展示 todo 列表、已完成数量、完成率用 computed思考一下如果要做「会话列表 StoreConversationStore」你会怎么设计有哪些数据哪些方法下一章我们会学习网关层设计——这是前后端通信的「翻译官」把各种不同的通信方式统一成好用的接口。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询