computed与autorun区别 #
computed 和 autorun 都是 MobX 中响应式编程的重要组成部分,但它们有不同的用途和特点。理解它们的区别对于正确使用 MobX 至关重要。
核心区别 #
text
┌─────────────────────────────────────────────────────────────┐
│ computed vs autorun │
├─────────────────────────────────────────────────────────────┤
│ │
│ computed: │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ State │ ── │computed │ ── │ Value │ │
│ └─────────┘ └─────────┘ └─────────┘ │
│ │ │
│ ▼ │
│ 返回值(可被其他代码使用) │
│ │
│ autorun: │
│ ┌─────────┐ ┌─────────┐ ┌─────────────┐ │
│ │ State │ ── │ autorun │ ── │ Side Effect │ │
│ └─────────┘ └─────────┘ └─────────────┘ │
│ │ │
│ ▼ │
│ 副作用(不返回值) │
│ │
└─────────────────────────────────────────────────────────────┘
对比表 #
| 特性 | computed | autorun |
|---|---|---|
| 返回值 | 有(计算值) | 无 |
| 缓存 | 有 | 无 |
| 触发时机 | 被访问时 | 立即执行 |
| 用途 | 派生数据 | 副作用 |
| 纯净性 | 必须是纯函数 | 可以有副作用 |
computed详解 #
特点 #
- 返回值:computed 总是返回一个值
- 缓存:只有依赖变化时才重新计算
- 惰性:只有被访问时才计算
- 纯净:应该是纯函数,没有副作用
使用场景 #
javascript
import { makeAutoObservable } from 'mobx';
class Store {
firstName = 'John';
lastName = 'Doe';
constructor() {
makeAutoObservable(this);
}
// 计算值 - 返回派生数据
get fullName() {
return `${this.firstName} ${this.lastName}`;
}
// 计算值 - 用于显示
get displayName() {
return this.fullName.toUpperCase();
}
// 计算值 - 用于验证
get isValid() {
return this.firstName.length > 0 && this.lastName.length > 0;
}
}
const store = new Store();
// 访问计算值
console.log(store.fullName); // 'John Doe'
console.log(store.displayName); // 'JOHN DOE'
console.log(store.isValid); // true
autorun详解 #
特点 #
- 无返回值:autorun 不返回值
- 无缓存:每次依赖变化都执行
- 立即执行:创建时立即执行一次
- 副作用:用于执行副作用
使用场景 #
javascript
import { makeAutoObservable, autorun } from 'mobx';
class Store {
count = 0;
constructor() {
makeAutoObservable(this);
}
}
const store = new Store();
// 自动运行 - 执行副作用
autorun(() => {
console.log(`Count changed to: ${store.count}`);
// 立即输出: Count changed to: 0
});
store.count = 1; // 输出: Count changed to: 1
store.count = 2; // 输出: Count changed to: 2
典型使用场景对比 #
场景1:派生数据 #
javascript
// ✅ 使用 computed - 派生数据
class TodoStore {
todos = [];
constructor() {
makeAutoObservable(this);
}
get completedCount() {
return this.todos.filter(t => t.completed).length;
}
get activeCount() {
return this.todos.filter(t => !t.completed).length;
}
}
// ❌ 不应该使用 autorun
autorun(() => {
// 这不是派生数据,而是副作用
store.completedCount = store.todos.filter(t => t.completed).length;
});
场景2:日志记录 #
javascript
// ✅ 使用 autorun - 日志副作用
autorun(() => {
console.log(`State changed: ${JSON.stringify(toJS(store))}`);
});
// ❌ 不应该使用 computed
get logState() {
console.log(`State changed: ${JSON.stringify(toJS(this))}`);
return null; // 这不是 computed 的正确用法
}
场景3:本地存储 #
javascript
// ✅ 使用 autorun - 保存到 localStorage
autorun(() => {
localStorage.setItem('todos', JSON.stringify(toJS(store.todos)));
});
// ❌ 不应该使用 computed
get saveTodos() {
localStorage.setItem('todos', JSON.stringify(toJS(this.todos)));
return null; // 副作用不应该在 computed 中
}
场景4:网络请求 #
javascript
// ✅ 使用 autorun - 触发网络请求
autorun(() => {
if (store.searchQuery) {
fetch(`/api/search?q=${store.searchQuery}`)
.then(res => res.json())
.then(data => runInAction(() => store.results = data));
}
});
// ❌ 不应该使用 computed
get searchResults() {
if (this.searchQuery) {
// 不能在 computed 中进行异步操作
fetch(`/api/search?q=${this.searchQuery}`);
}
return this.results;
}
组合使用 #
computed 和 autorun 可以组合使用:
javascript
import { makeAutoObservable, autorun } from 'mobx';
class Store {
items = [];
filter = 'all';
constructor() {
makeAutoObservable(this);
// 使用 autorun 监听计算值变化
autorun(() => {
console.log(`Active items: ${this.activeItems.length}`);
});
}
// 计算值
get activeItems() {
return this.items.filter(item => item.active);
}
get filteredItems() {
switch (this.filter) {
case 'active':
return this.activeItems;
default:
return this.items;
}
}
}
完整示例 #
实时搜索 #
javascript
import { makeAutoObservable, autorun, runInAction } from 'mobx';
class SearchStore {
query = '';
results = [];
loading = false;
constructor() {
makeAutoObservable(this);
// 使用 autorun 实现防抖搜索
let timeout;
autorun(() => {
if (timeout) clearTimeout(timeout);
timeout = setTimeout(async () => {
if (this.query.trim()) {
this.loading = true;
try {
const response = await fetch(`/api/search?q=${this.query}`);
const results = await response.json();
runInAction(() => {
this.results = results;
this.loading = false;
});
} catch (error) {
runInAction(() => {
this.loading = false;
});
}
} else {
this.results = [];
}
}, 300);
});
}
// 计算值 - 显示结果数量
get resultCount() {
return this.results.length;
}
// 计算值 - 是否有结果
get hasResults() {
return this.results.length > 0;
}
setQuery(query) {
this.query = query;
}
}
状态持久化 #
javascript
import { makeAutoObservable, autorun, toJS } from 'mobx';
class PersistentStore {
user = null;
settings = {};
constructor() {
makeAutoObservable(this);
// 从 localStorage 恢复状态
this.loadFromStorage();
// 使用 autorun 自动保存
autorun(() => {
this.saveToStorage();
});
}
// 计算值 - 是否已登录
get isLoggedIn() {
return this.user !== null;
}
// 计算值 - 用户显示名
get displayName() {
return this.user?.name || 'Guest';
}
// 副作用 - 保存到 localStorage
saveToStorage() {
const data = {
user: toJS(this.user),
settings: toJS(this.settings)
};
localStorage.setItem('app_state', JSON.stringify(data));
}
loadFromStorage() {
const data = localStorage.getItem('app_state');
if (data) {
const parsed = JSON.parse(data);
this.user = parsed.user;
this.settings = parsed.settings;
}
}
// Actions
setUser(user) {
this.user = user;
}
updateSettings(settings) {
Object.assign(this.settings, settings);
}
logout() {
this.user = null;
}
}
选择指南 #
使用computed当: #
- 需要从现有状态派生新值
- 需要缓存计算结果
- 需要在多个地方使用同一个派生值
- 计算是纯函数
javascript
// ✅ computed
get fullName() {
return `${this.firstName} ${this.lastName}`;
}
get filteredItems() {
return this.items.filter(item => item.active);
}
get totalPrice() {
return this.items.reduce((sum, item) => sum + item.price, 0);
}
使用autorun当: #
- 需要执行副作用
- 需要响应状态变化执行操作
- 不需要返回值
- 需要立即执行
javascript
// ✅ autorun
autorun(() => {
document.title = `${store.unreadCount} unread messages`;
});
autorun(() => {
localStorage.setItem('token', store.token);
});
autorun(() => {
console.log('State changed:', toJS(store));
});
总结 #
| 场景 | 使用 |
|---|---|
| 派生数据 | computed |
| 格式化显示 | computed |
| 数据验证 | computed |
| 统计计算 | computed |
| 日志记录 | autorun |
| 本地存储 | autorun |
| DOM操作 | autorun |
| 网络请求 | autorun |
记住:computed 用于计算值,autorun 用于执行副作用。
继续学习 Autorun自动运行,深入了解响应式副作用。
最后更新:2026-03-28