你是否遇到过这样的业务需求:在开发网页时,需要在多个不同的地方共享和修改同一个数据?比如用户的登录状态、购物车商品数量,或者一个弹窗的显示/隐藏状态?这种场景使用 MobX Store 也许会是一个不错的解决方案。
本文用最简单的方式,一步步理解什么是 MobX Store,以及如何使用它。
什么是 MobX Store?
先理解问题
假设正在开发一个简单的购物网站。网站上有这些元素:
- 顶部导航栏显示购物车商品数量
- 商品列表页面可以添加商品到购物车
- 购物车页面显示所有已添加的商品
如果不使用任何状态管理工具,你可能会这样做:
// 在全局变量中存储购物车数据
let cartItems = [];
// 在添加商品按钮的点击事件中
function addToCart(product) {
cartItems.push(product);
// 然后手动更新所有需要显示购物车数据的地方
updateNavBar();
updateCartPage();
}
// 在每个需要显示数据的地方都写更新函数
function updateNavBar() {
document.getElementById("cart-count").textContent = cartItems.length;
}
function updateCartPage() {
const cartList = document.getElementById("cart-list");
cartList.innerHTML = cartItems
.map((item) => `<li>${item.name}</li>`)
.join("");
}
这种方式有几个明显的问题:
- 代码分散,难以维护
- 每次数据变化都需要手动更新所有相关的显示
- 容易遗漏某些地方的更新
MobX Store 提供哪些功能?
MobX Store 就是为了解决这些问题而生的。它就像一个集中的数据仓库,具有以下特点:
- 统一管理数据
- 自动追踪数据变化
- 自动更新界面
MobX 的两种写法
在开始之前,需要说明 MobX 有两种常见的写法:装饰器写法和 makeObservable 写法。
1. 装饰器写法(旧版本常见)
import { observable, action } from "mobx";
class CounterStore {
@observable count = 0; // 使用@observable装饰器标记数据
@action
increment() {
// 使用@action装饰器标记方法
this.count += 1;
}
@action
decrement() {
this.count -= 1;
}
}
2. makeObservable 写法(新版本推荐)
import { makeObservable, observable, action } from "mobx";
class CounterStore {
count = 0;
constructor() {
makeObservable(this, {
count: observable, // 在构造函数中声明observable
increment: action, // 在构造函数中声明action
decrement: action,
});
}
increment() {
this.count += 1;
}
decrement() {
this.count -= 1;
}
}
两种写法效果完全相同,但是推荐使用第二种 makeObservable 写法,原因是:
- 装饰器(@)语法还不是 JavaScript 的标准特性
- 使用装饰器需要特殊的 babel 配置
- 某些开发环境可能不支持装饰器语法
- makeObservable 写法更容易配置和使用
在本文的示例中,我们使用 makeObservable 写法,这样可以直接复制代码来使用,无需额外配置。
一个简单的例子
让我们通过一个最简单的例子来理解 MobX Store:创建一个带计数器的页面。
1. 首先安装必要的包
npm install mobx mobx-react react react-dom
2. 创建一个 Store
// counterStore.js
import { makeObservable, observable, action } from "mobx";
class CounterStore {
// 存储的数据
count = 0;
constructor() {
// 告诉MobX要追踪哪些数据和方法
makeObservable(this, {
count: observable, // 这个数据需要被追踪
increment: action, // 这个方法会修改数据
decrement: action, // 这个方法会修改数据
});
}
// 定义修改数据的方法
increment() {
this.count += 1;
}
decrement() {
this.count -= 1;
}
}
// 创建一个实例并导出
export const counterStore = new CounterStore();
3. 在 React 应用中使用 Store
// App.js
import React from "react";
import { Provider } from "mobx-react";
import { counterStore } from "./counterStore";
import Counter from "./Counter";
function App() {
return (
// 使用Provider提供store
<Provider counter={counterStore}>
<Counter />
</Provider>
);
}
4. 创建计数器组件
// Counter.js
import React from "react";
import { observer } from "mobx-react";
// observer使组件能响应store的变化
function Counter({ counter }) {
return (
<div>
<h1>计数器: {counter.count}</h1>
<button onClick={() => counter.increment()}>+1</button>
<button onClick={() => counter.decrement()}>-1</button>
</div>
);
}
export default observer(Counter);
工作原理解释
- Store 的创建
- Store 就像一个专门的数据管理员
observable
标记的数据会被 MobX 追踪变化action
标记的方法用来修改数据
- Provider 的作用
- Provider 就像一个快递员
- 它把 store 送到需要用到这些数据的组件那里
- Observer 的作用
- Observer 就像一个监视器
- 当 store 中的数据变化时,它会自动通知组件更新显示
实际应用举例
让我们看一个更实用的例子:购物车功能
// cartStore.js
import { makeObservable, observable, action, computed } from "mobx";
class CartStore {
items = [];
constructor() {
makeObservable(this, {
items: observable,
addItem: action,
removeItem: action,
totalPrice: computed,
});
}
addItem(product) {
this.items.push(product);
}
removeItem(productId) {
this.items = this.items.filter((item) => item.id !== productId);
}
// computed用于计算派生数据
get totalPrice() {
return this.items.reduce((total, item) => total + item.price, 0);
}
}
export const cartStore = new CartStore();
// CartComponent.js
import React from "react";
import { observer } from "mobx-react";
function CartComponent({ cart }) {
return (
<div>
<h2>购物车</h2>
{cart.items.map((item) => (
<div key={item.id}>
<span>{item.name}</span>
<span>${item.price}</span>
<button onClick={() => cart.removeItem(item.id)}>删除</button>
</div>
))}
<div>总价: ${cart.totalPrice}</div>
</div>
);
}
export default observer(CartComponent);
最佳实践
- Store 的设计原则
- 每个 Store 只负责一个明确的功能领域
- 相关的数据和方法放在一起
- 用 computed 处理派生数据
- 何时使用 MobX Store
- 当多个组件需要共享状态时
- 当状态需要在组件外持久存在时
- 当状态逻辑较复杂时
- 注意事项
- 只在 action 中修改数据
- 保持 Store 结构简单清晰
- 避免在 Store 中存储可以由现有数据计算出的内容
总结
MobX Store 是一个强大的状态管理工具,它通过集中管理数据、自动追踪变化和更新界面,大大简化了 React 应用的开发。对于初学者来说,可以把它理解为一个智能的数据管理员,它知道:
- 在哪里存储数据
- 谁修改了数据
- 谁需要知道数据变化了
另外,不是所有项目都需要用到 MobX Store。对于简单的项目,使用 React 自带的 state 可能就足够了。当你发现在多个地方需要共享和修改同一个数据时,那就是考虑使用 MobX Store 的好时机。