MobX核心概念 #

MobX 的核心概念可以概括为三个部分:State(状态)Actions(动作)Derivations(派生)。理解这三个概念是掌握 MobX 的关键。

核心概念概览 #

text
┌─────────────────────────────────────────────────────────────┐
│                        MobX 架构                            │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│    ┌─────────┐         ┌─────────────┐                     │
│    │ Actions │ ──────► │    State    │                     │
│    │  动作   │         │    状态     │                     │
│    └─────────┘         └──────┬──────┘                     │
│                              │                              │
│                              ▼                              │
│                    ┌─────────────────┐                     │
│                    │  Derivations    │                     │
│                    │     派生        │                     │
│                    └────────┬────────┘                     │
│                             │                               │
│              ┌──────────────┼──────────────┐               │
│              ▼              ▼              ▼               │
│        ┌──────────┐  ┌──────────┐  ┌──────────┐           │
│        │ Computed │  │ Reactions│  │   UI     │           │
│        │  计算值  │  │   反应   │  │  界面    │           │
│        └──────────┘  └──────────┘  └──────────┘           │
│                                                             │
└─────────────────────────────────────────────────────────────┘

一、State(状态) #

State 是应用的数据源,是驱动应用运行的核心。在 MobX 中,状态是可观察的(Observable)。

定义状态 #

javascript
import { makeAutoObservable, observable } from 'mobx';

// 方式一:使用 makeAutoObservable(推荐)
class TodoStore {
  todos = [];
  filter = 'all';

  constructor() {
    makeAutoObservable(this);
  }
}

// 方式二:使用 observable 函数
const state = observable({
  count: 0,
  name: 'MobX'
});

// 方式三:使用装饰器(需要配置)
class Store {
  @observable count = 0;
  @observable name = 'MobX';
}

状态的特点 #

  1. 可观察性:状态变化会被自动追踪
  2. 响应式:状态变化会触发相关更新
  3. 可变性:可以直接修改状态
javascript
const store = new TodoStore();

// 直接修改状态
store.todos.push({ id: 1, title: 'Learn MobX' });
store.filter = 'completed';

可观察的数据类型 #

MobX 可以让以下数据类型变为可观察:

javascript
import { makeAutoObservable } from 'mobx';

class Store {
  // 基本类型
  string = 'hello';
  number = 42;
  boolean = true;

  // 对象
  object = { name: 'MobX', version: 6 };

  // 数组
  array = [1, 2, 3];

  // Map 和 Set
  map = new Map();
  set = new Set();

  constructor() {
    makeAutoObservable(this);
  }
}

二、Actions(动作) #

Actions 是修改状态的唯一途径。使用 Action 可以让状态变化变得可追踪、可调试。

定义动作 #

javascript
import { makeAutoObservable, action } from 'mobx';

class CounterStore {
  count = 0;

  constructor() {
    makeAutoObservable(this);
  }

  // 方式一:方法自动成为 action
  increment() {
    this.count++;
  }

  decrement() {
    this.count--;
  }

  // 方式二:使用 action 包装
  reset = action(() => {
    this.count = 0;
  });
}

动作的特点 #

  1. 批量更新:多个状态修改只触发一次更新
  2. 事务性:状态修改是原子操作
  3. 可追踪:便于调试和日志记录
javascript
class TodoStore {
  todos = [];

  constructor() {
    makeAutoObservable(this);
  }

  // 批量修改,只触发一次更新
  addTodo(title) {
    this.todos.push({
      id: Date.now(),
      title,
      completed: false
    });
  }

  // 复杂操作
  toggleTodo(id) {
    const todo = this.todos.find(t => t.id === id);
    if (todo) {
      todo.completed = !todo.completed;
    }
  }
}

action.bound #

使用 action.bound 自动绑定 this

javascript
class Store {
  count = 0;

  constructor() {
    makeAutoObservable(this);
  }

  // 自动绑定 this
  increment = action(() => {
    this.count++;
  });

  // 或者使用 makeAutoObservable 的 autoBind 选项
  // makeAutoObservable(this, {}, { autoBind: true });
}

const store = new Store();
const { increment } = store;
increment(); // this 正确绑定

异步动作 #

处理异步操作时,推荐使用 runInAction

javascript
import { makeAutoObservable, runInAction } from 'mobx';

class UserStore {
  users = [];
  loading = false;

  constructor() {
    makeAutoObservable(this);
  }

  async fetchUsers() {
    this.loading = true;

    try {
      const response = await fetch('/api/users');
      const users = await response.json();

      // 在 action 中修改状态
      runInAction(() => {
        this.users = users;
        this.loading = false;
      });
    } catch (error) {
      runInAction(() => {
        this.loading = false;
      });
    }
  }
}

三、Derivations(派生) #

Derivations 是从状态派生出来的值或副作用。MobX 会自动追踪派生对状态的依赖,并在状态变化时自动更新。

Computed Values(计算值) #

计算值是从状态派生出来的值,具有缓存特性:

javascript
import { makeAutoObservable, computed } from 'mobx';

class TodoStore {
  todos = [];
  filter = 'all';

  constructor() {
    makeAutoObservable(this);
  }

  // 计算值 - 自动缓存
  get filteredTodos() {
    switch (this.filter) {
      case 'completed':
        return this.todos.filter(t => t.completed);
      case 'active':
        return this.todos.filter(t => !t.completed);
      default:
        return this.todos;
    }
  }

  // 另一个计算值
  get completedCount() {
    return this.todos.filter(t => t.completed).length;
  }

  get totalCount() {
    return this.todos.length;
  }
}

计算值的特点 #

  1. 自动缓存:只有依赖变化时才重新计算
  2. 惰性求值:只有被访问时才计算
  3. 响应式:依赖变化时自动更新
javascript
const store = new TodoStore();

// 第一次访问,计算并缓存
console.log(store.filteredTodos);

// 再次访问,直接返回缓存值
console.log(store.filteredTodos);

// 修改状态,缓存失效
store.filter = 'completed';

// 重新计算
console.log(store.filteredTodos);

Reactions(反应) #

Reactions 是状态变化时自动执行的副作用:

autorun #

javascript
import { autorun } from 'mobx';

const store = new TodoStore();

// 自动运行,状态变化时重新执行
const dispose = autorun(() => {
  console.log(`Total: ${store.todos.length}`);
  console.log(`Completed: ${store.completedCount}`);
});

// 清除
dispose();

reaction #

javascript
import { reaction } from 'mobx';

// 精确追踪特定状态
const dispose = reaction(
  // 追踪函数 - 返回要追踪的值
  () => store.todos.length,
  // 副作用函数 - 值变化时执行
  (length, previousLength) => {
    console.log(`Todos changed from ${previousLength} to ${length}`);
  }
);

when #

javascript
import { when } from 'mobx';

// 条件满足时执行一次
when(
  () => store.todos.length > 10,
  () => {
    console.log('Todos exceeded 10!');
  }
);

完整示例 #

下面是一个完整的 Todo 应用示例:

javascript
import { makeAutoObservable, computed, autorun } from 'mobx';
import { observer } from 'mobx-react-lite';

// Store
class TodoStore {
  todos = [];
  filter = 'all';

  constructor() {
    makeAutoObservable(this);
  }

  // Actions
  addTodo(title) {
    this.todos.push({
      id: Date.now(),
      title,
      completed: false
    });
  }

  toggleTodo(id) {
    const todo = this.todos.find(t => t.id === id);
    if (todo) {
      todo.completed = !todo.completed;
    }
  }

  setFilter(filter) {
    this.filter = filter;
  }

  // Computed
  get filteredTodos() {
    switch (this.filter) {
      case 'completed':
        return this.todos.filter(t => t.completed);
      case 'active':
        return this.todos.filter(t => !t.completed);
      default:
        return this.todos;
    }
  }

  get completedCount() {
    return this.todos.filter(t => t.completed).length;
  }

  get activeCount() {
    return this.todos.length - this.completedCount;
  }
}

// 创建实例
const todoStore = new TodoStore();

// Reaction - 自动保存到 localStorage
autorun(() => {
  localStorage.setItem('todos', JSON.stringify(todoStore.todos));
});

// React 组件
const TodoApp = observer(() => (
  <div>
    <h1>Todos ({todoStore.activeCount} remaining)</h1>
    <ul>
      {todoStore.filteredTodos.map(todo => (
        <li
          key={todo.id}
          onClick={() => todoStore.toggleTodo(todo.id)}
          style={{ textDecoration: todo.completed ? 'line-through' : 'none' }}
        >
          {todo.title}
        </li>
      ))}
    </ul>
    <button onClick={() => todoStore.setFilter('all')}>All</button>
    <button onClick={() => todoStore.setFilter('active')}>Active</button>
    <button onClick={() => todoStore.setFilter('completed')}>Completed</button>
  </div>
));

核心原则 #

MobX 遵循以下核心原则:

1. 单一数据源 #

javascript
// 推荐:单一 Store
const store = new AppStore();

// 或按功能拆分
const userStore = new UserStore();
const todoStore = new TodoStore();

2. 状态只读,通过 Action 修改 #

javascript
// 错误:直接修改(虽然可以工作)
store.count++;

// 正确:通过 Action 修改
store.increment();

3. 派生数据使用 Computed #

javascript
// 错误:存储派生值
class Store {
  todos = [];
  completedCount = 0; // 不应该存储

  updateCompletedCount() {
    this.completedCount = this.todos.filter(t => t.completed).length;
  }
}

// 正确:使用计算值
class Store {
  todos = [];

  get completedCount() {
    return this.todos.filter(t => t.completed).length;
  }
}

总结 #

MobX 的三大核心概念:

概念 说明 特点
State 应用状态 可观察、可变
Actions 修改状态 批量更新、可追踪
Derivations 派生数据 自动更新、缓存

理解这三个概念是掌握 MobX 的基础。继续学习 Observable状态,深入了解如何创建可观察状态。

最后更新:2026-03-28