什么是MobX Store?有何用途?前端数据状态管理从入门到实践

从零开始学 MobX Store:简化 React 数据管理

文章目录

你是否遇到过这样的业务需求:在开发网页时,需要在多个不同的地方共享和修改同一个数据?比如用户的登录状态、购物车商品数量,或者一个弹窗的显示/隐藏状态?这种场景使用 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("");
}

这种方式有几个明显的问题:

  1. 代码分散,难以维护
  2. 每次数据变化都需要手动更新所有相关的显示
  3. 容易遗漏某些地方的更新

MobX Store 提供哪些功能?

MobX Store 就是为了解决这些问题而生的。它就像一个集中的数据仓库,具有以下特点:

  1. 统一管理数据
  2. 自动追踪数据变化
  3. 自动更新界面

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 写法,原因是:

  1. 装饰器(@)语法还不是 JavaScript 的标准特性
  2. 使用装饰器需要特殊的 babel 配置
  3. 某些开发环境可能不支持装饰器语法
  4. 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);

工作原理解释

  1. Store 的创建
    • Store 就像一个专门的数据管理员
    • observable标记的数据会被 MobX 追踪变化
    • action标记的方法用来修改数据
  2. Provider 的作用
    • Provider 就像一个快递员
    • 它把 store 送到需要用到这些数据的组件那里
  3. 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);

最佳实践

  1. Store 的设计原则
    • 每个 Store 只负责一个明确的功能领域
    • 相关的数据和方法放在一起
    • 用 computed 处理派生数据
  2. 何时使用 MobX Store
    • 当多个组件需要共享状态时
    • 当状态需要在组件外持久存在时
    • 当状态逻辑较复杂时
  3. 注意事项
    • 只在 action 中修改数据
    • 保持 Store 结构简单清晰
    • 避免在 Store 中存储可以由现有数据计算出的内容

总结

MobX Store 是一个强大的状态管理工具,它通过集中管理数据、自动追踪变化和更新界面,大大简化了 React 应用的开发。对于初学者来说,可以把它理解为一个智能的数据管理员,它知道:

  • 在哪里存储数据
  • 谁修改了数据
  • 谁需要知道数据变化了

另外,不是所有项目都需要用到 MobX Store。对于简单的项目,使用 React 自带的 state 可能就足够了。当你发现在多个地方需要共享和修改同一个数据时,那就是考虑使用 MobX Store 的好时机。


也可以看看