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 | makeAutoObservable 或 observable |
支持所有原生方法 |
| Map | new Map() + makeAutoObservable |
键值对存储 |
| Set | new Set() + makeAutoObservable |
唯一值集合 |
使用建议:
- 列表数据:使用 Array
- 键值映射:使用 Map
- 唯一集合:使用 Set
- 需要传递给外部:使用
toJS转换
继续学习 工具函数,了解 MobX 提供的常用工具方法。
最后更新:2026-03-28