Observable集合 #

MobX 提供了对 JavaScript 集合类型(Array、Map、Set)的响应式支持。了解如何正确使用可观察集合对于构建高效的应用至关重要。

Observable数组 #

创建可观察数组 #

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

// 方式1:在类中
class Store {
  items = [];

  constructor() {
    makeAutoObservable(this);
  }
}

// 方式2:使用 observable
const items = observable([1, 2, 3]);

数组方法 #

可观察数组支持所有原生数组方法:

javascript
import { makeAutoObservable } from 'mobx';

class ListStore {
  items = [1, 2, 3];

  constructor() {
    makeAutoObservable(this);
  }

  demo() {
    // 添加元素
    this.items.push(4);           // 末尾添加
    this.items.unshift(0);        // 开头添加

    // 删除元素
    this.items.pop();             // 删除末尾
    this.items.shift();           // 删除开头
    this.items.splice(1, 1);      // 删除指定位置

    // 查找
    this.items.find(x => x > 1);  // 查找元素
    this.items.findIndex(x => x > 1);  // 查找索引
    this.items.filter(x => x > 1);     // 过滤
    this.items.map(x => x * 2);        // 映射

    // 修改
    this.items.sort((a, b) => b - a);  // 排序
    this.items.reverse();              // 反转

    // 其他
    this.items.includes(2);       // 是否包含
    this.items.indexOf(2);        // 索引
    this.items.join(',');         // 转字符串
    this.items.reduce((a, b) => a + b, 0);  // 归约
  }
}

MobX特有方法 #

javascript
import { makeAutoObservable } from 'mobx';

class Store {
  items = [1, 2, 3];

  constructor() {
    makeAutoObservable(this);
  }

  demo() {
    // replace - 替换整个数组内容
    this.items.replace([4, 5, 6]);

    // clear - 清空数组
    this.items.clear();

    // remove - 移除指定元素
    this.items.remove(2);  // 移除值为 2 的元素

    // toJS - 转换为普通数组
    const plainArray = this.items.toJS();
  }
}

完整示例:Todo列表 #

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

class TodoStore {
  todos = [];

  constructor() {
    makeAutoObservable(this);
  }

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

  removeTodo(id) {
    const index = this.todos.findIndex(t => t.id === id);
    if (index > -1) {
      this.todos.splice(index, 1);
    }
  }

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

  updateTodo(id, updates) {
    const todo = this.todos.find(t => t.id === id);
    if (todo) {
      Object.assign(todo, updates);
    }
  }

  clearCompleted() {
    this.todos = this.todos.filter(t => !t.completed);
  }

  toggleAll(completed) {
    this.todos.forEach(todo => {
      todo.completed = completed;
    });
  }

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

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

const todoStore = new TodoStore();

const TodoList = observer(() => (
  <ul>
    {todoStore.todos.map(todo => (
      <li
        key={todo.id}
        style={{ textDecoration: todo.completed ? 'line-through' : 'none' }}
      >
        {todo.title}
        <button onClick={() => todoStore.toggleTodo(todo.id)}>Toggle</button>
        <button onClick={() => todoStore.removeTodo(todo.id)}>Delete</button>
      </li>
    ))}
  </ul>
));

Observable Map #

创建可观察Map #

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

// 方式1:在类中
class Store {
  userMap = new Map();

  constructor() {
    makeAutoObservable(this);
  }
}

// 方式2:使用 observable.map
const userMap = observable.map({ 'user1': { name: 'John' } });

Map方法 #

javascript
import { makeAutoObservable } from 'mobx';

class UserStore {
  users = new Map();

  constructor() {
    makeAutoObservable(this);
  }

  demo() {
    // 设置
    this.users.set('user1', { name: 'John' });
    this.users.set('user2', { name: 'Jane' });

    // 获取
    const user = this.users.get('user1');

    // 检查存在
    this.users.has('user1');  // true

    // 删除
    this.users.delete('user1');

    // 清空
    this.users.clear();

    // 大小
    this.users.size;

    // 遍历
    this.users.forEach((value, key) => {
      console.log(key, value);
    });

    // 转换
    const keys = Array.from(this.users.keys());
    const values = Array.from(this.users.values());
    const entries = Array.from(this.users.entries());

    // toJS
    const plainMap = this.users.toJS();
  }
}

完整示例:缓存Store #

javascript
import { makeAutoObservable } from 'mobx';

class CacheStore {
  cache = new Map();
  loading = new Set();

  constructor() {
    makeAutoObservable(this);
  }

  async get(key, fetchFn) {
    // 检查缓存
    if (this.cache.has(key)) {
      return this.cache.get(key);
    }

    // 检查是否正在加载
    if (this.loading.has(key)) {
      return null;
    }

    // 开始加载
    this.loading.add(key);

    try {
      const data = await fetchFn();
      this.cache.set(key, data);
      return data;
    } finally {
      this.loading.delete(key);
    }
  }

  set(key, value) {
    this.cache.set(key, value);
  }

  has(key) {
    return this.cache.has(key);
  }

  delete(key) {
    this.cache.delete(key);
  }

  clear() {
    this.cache.clear();
  }

  get size() {
    return this.cache.size;
  }

  get isLoading() {
    return this.loading.size > 0;
  }
}

export default new CacheStore();

Observable Set #

创建可观察Set #

javascript
import { makeAutoObservable } from 'mobx';

class Store {
  selectedIds = new Set();

  constructor() {
    makeAutoObservable(this);
  }
}

Set方法 #

javascript
import { makeAutoObservable } from 'mobx';

class SelectionStore {
  selected = new Set();

  constructor() {
    makeAutoObservable(this);
  }

  demo() {
    // 添加
    this.selected.add(1);
    this.selected.add(2);

    // 检查存在
    this.selected.has(1);  // true

    // 删除
    this.selected.delete(1);

    // 清空
    this.selected.clear();

    // 大小
    this.selected.size;

    // 遍历
    this.selected.forEach(value => {
      console.log(value);
    });

    // 转换
    const array = Array.from(this.selected);

    // toJS
    const plainSet = this.selected.toJS();
  }

  toggle(id) {
    if (this.selected.has(id)) {
      this.selected.delete(id);
    } else {
      this.selected.add(id);
    }
  }

  selectAll(ids) {
    ids.forEach(id => this.selected.add(id));
  }

  deselectAll() {
    this.selected.clear();
  }
}

完整示例:多选列表 #

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

class MultiSelectStore {
  items = [];
  selected = new Set();

  constructor() {
    makeAutoObservable(this);
  }

  setItems(items) {
    this.items = items;
    this.selected.clear();
  }

  toggle(id) {
    if (this.selected.has(id)) {
      this.selected.delete(id);
    } else {
      this.selected.add(id);
    }
  }

  selectAll() {
    this.items.forEach(item => this.selected.add(item.id));
  }

  deselectAll() {
    this.selected.clear();
  }

  isSelected(id) {
    return this.selected.has(id);
  }

  get selectedCount() {
    return this.selected.size;
  }

  get selectedItems() {
    return this.items.filter(item => this.selected.has(item.id));
  }

  get isAllSelected() {
    return this.items.length > 0 && this.selected.size === this.items.length;
  }

  get isPartialSelected() {
    return this.selected.size > 0 && this.selected.size < this.items.length;
  }
}

const selectStore = new MultiSelectStore();

const MultiSelectList = observer(() => (
  <div>
    <div>
      <input
        type="checkbox"
        checked={selectStore.isAllSelected}
        ref={input => {
          if (input) input.indeterminate = selectStore.isPartialSelected;
        }}
        onChange={() => {
          if (selectStore.isAllSelected) {
            selectStore.deselectAll();
          } else {
            selectStore.selectAll();
          }
        }}
      />
      <span>{selectStore.selectedCount} selected</span>
    </div>

    <ul>
      {selectStore.items.map(item => (
        <li key={item.id}>
          <input
            type="checkbox"
            checked={selectStore.isSelected(item.id)}
            onChange={() => selectStore.toggle(item.id)}
          />
          {item.name}
        </li>
      ))}
    </ul>
  </div>
));

注意事项 #

1. 数组索引访问 #

javascript
// 正确:索引访问
const item = store.items[0];

// 正确:动态索引
const item = store.items[index];

// 注意:越界访问返回 undefined
const item = store.items[100];  // undefined

2. 长度属性 #

javascript
// 正确:访问 length
const len = store.items.length;

// 注意:直接设置 length 会截断数组
store.items.length = 0;  // 清空数组

3. 嵌套对象 #

javascript
// 默认:嵌套对象也是可观察的
const store = makeAutoObservable({
  items: [{ name: 'Item' }]
});

store.items[0].name = 'New Name';  // 响应式更新

// 浅层观察
const shallowStore = observable.shallow({
  items: [{ name: 'Item' }]
});

shallowStore.items[0].name = 'New Name';  // 不触发更新
shallowStore.items.push({ name: 'New' });  // 触发更新

4. toJS转换 #

javascript
import { toJS } from 'mobx';

// 转换为普通 JavaScript 对象
const plainArray = toJS(store.items);
const plainMap = toJS(store.userMap);
const plainSet = toJS(store.selected);

总结 #

集合类型 创建方式 特点
Array makeAutoObservableobservable 支持所有原生方法
Map new Map() + makeAutoObservable 键值对存储
Set new Set() + makeAutoObservable 唯一值集合

使用建议:

  • 列表数据:使用 Array
  • 键值映射:使用 Map
  • 唯一集合:使用 Set
  • 需要传递给外部:使用 toJS 转换

继续学习 工具函数,了解 MobX 提供的常用工具方法。

最后更新:2026-03-28