在前端应用开发中,状态管理一直是核心议题。随着应用复杂度不断攀升,如何高效、可维护地管理应用状态成为开发者必须面对的挑战。MobX 作为一款响应式状态管理库,以其简洁的API和优秀的性能表现赢得了众多开发者的青睐。本文将带你深入了解 MobX 的核心概念,并通过实际案例展示如何与 React 协同构建响应式应用。
# 一、为什么选择MobX
# 1.1 传统状态管理的痛点
在 MobX 出现之前,开发者通常使用 Redux 来管理应用状态。Redux 采用单一数据源模式,所有状态存储在一个只读的 store 中,只能通过 action 和 reducer 来更新状态。这种方式虽然逻辑清晰,但在实际应用中存在一些痛点:
状态冗余更新问题:由于 Redux 的状态是不可变的,每次更新都需要创建新的状态对象。这导致与 store 连接的 UI 组件都会重新渲染,即使它们只依赖状态中的部分数据。虽然可以使用 PureRenderMixin 或 reselect 来优化,但增加了额外的开发成本。
样板代码繁多:使用 Redux 需要编写大量的 action types、action creators、reducers,即使是一个简单的功能也需要完整的模板代码。
学习曲线陡峭:理解 Redux 的数据流需要掌握 action、reducer、middleware 等概念,对新手不太友好。
# 1.2 MobX的核心理念
MobX 采用了完全不同的设计理念:利用 JavaScript 的代理(Proxy)机制,自动追踪状态的变化并更新依赖它的组件。这种方式被称为响应式编程。
MobX 的核心理念可以概括为三点:
-
可观察的状态(Observable State):将普通的
JavaScript对象转换为可观察的对象,任何对状态的修改都能被追踪。 -
自动推导(Computed Values):根据现有状态自动计算衍生值,类似 Excel 的公式功能。
-
响应式副作用(Reactions):当状态变化时自动执行副作用操作,如更新
UI、打印日志、发起网络请求等。
# 1.3 MobX与Redux对比
| 特性 | MobX | Redux |
|---|---|---|
| 数据结构 | 多 store,支持普通对象 |
单一 store,不可变数据 |
| 更新方式 | 直接修改,可选严格模式 | 只能通过 action 和 reducer |
| 依赖追踪 | 自动追踪,按需更新 | 手动订阅,可能过度渲染 |
| 代码量 | 简洁,样板代码少 | 较多模板代码 |
| 学习成本 | 较低 | 较高 |
| 调试工具 | 友好的时间旅行调试 | 强大的 DevTools |
从数据流角度看,Redux 管理的是 STORE -> VIEW -> ACTION 的完整闭环,而 MobX 更关注 STORE -> VIEW 的部分。action 在 MobX 中是可选的,你可以直接修改状态,但这也带来了一个潜在问题:状态的修改入口不统一。
# 1.4 MobX的优缺点分析
优点
第一点是基于运行时的数据订阅。MobX 的数据依赖始终保持最小,而且是基于运行时自动追踪的。相比之下,使用 Redux 时可能一不小心就多订阅或者少订阅了数据,导致性能问题。因此在使用 Redux 时,我们需要借助 PureRenderMixin 以及 reselect 对 selector 做缓存优化。
第二点是通过面向对象的方式组织领域模型。OOP 的方式在某些场景下会比较方便,尤其是容易抽取 domain model 的时候。由于 MobX 支持引用方式引用数据,可以非常容易形成模型图(model graph),这有助于更好地理解应用结构。
第三点是修改数据方便自然。MobX 基于原生的 JavaScript 对象、数组和 Class 实现,修改数据不需要额外语法成本,也不需要始终返回一个新的数据,而是直接操作数据。
缺点
第一点是缺少最佳实践和社区积累。MobX 相对较新,遇到的问题可能社区都没有遇到过。并且,MobX 并没有很好的扩展和插件机制。
第二点是随意修改 store 的风险。我们都知道 Redux 里唯一可以改数据的地方是 reducer,这样可以保证应用的安全稳定;而 MobX 可以随意修改数据,触发更新,给人一种不安全的感觉。不过最新的 MobX 2.2 版本加入了 action 支持,并且开启 strict mode 之后,就只有 action 可以对数据进行修改,限制数据的修改入口,可以解决这个问题。
第三点是逻辑层的限制。如果更新逻辑不能很好地封装在 domain class 里,用 Redux 会更合适。另外,MobX 缺少类似 redux-saga 的库,业务逻辑的整合不知道放哪合适。
# 二、核心API详解
# 2.1 observable - 定义可观察状态
@observable 装饰器用于将 JavaScript 对象属性转换为可观察的属性。被装饰的属性会暴露出来供观察者使用,当属性值发生变化时,所有依赖该属性的地方都会收到通知。
Observable 值可以是 JavaScript 基本数据类型、引用类型、普通对象、类实例、数组和映射。
import { makeAutoObservable } from 'mobx'
class Counter {
// 基本类型
count = 0
name = '计数器'
// 引用类型
user = {
name: '张三',
age<