Autorun自动运行 #
autorun 是 MobX 提供的一个函数,用于创建自动响应状态变化的副作用。当 autorun 中使用的可观察状态发生变化时,它会自动重新执行。
基本用法 #
简单示例 #
javascript
import { makeAutoObservable, autorun } from 'mobx';
class Store {
count = 0;
constructor() {
makeAutoObservable(this);
}
}
const store = new Store();
// 创建自动运行的副作用
autorun(() => {
console.log(`Count is: ${store.count}`);
});
// 立即输出: Count is: 0
// 修改状态,自动触发
store.count = 1; // 输出: Count is: 1
store.count = 2; // 输出: Count is: 2
工作原理 #
text
┌─────────────────────────────────────────────────────────────┐
│ autorun 工作流程 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 1. 创建 autorun │
│ │ │
│ ▼ │
│ 2. 立即执行一次,收集依赖 │
│ │ │
│ ▼ │
│ 3. 监听依赖变化 │
│ │ │
│ ▼ │
│ 4. 依赖变化时重新执行 │
│ │ │
│ └──────► 循环监听 │
│ │
└─────────────────────────────────────────────────────────────┘
清除autorun #
autorun 返回一个清除函数,调用它可以停止自动运行:
javascript
import { makeAutoObservable, autorun } from 'mobx';
const store = makeAutoObservable({ count: 0 });
// 返回清除函数
const dispose = autorun(() => {
console.log(`Count: ${store.count}`);
});
// 输出: Count: 0
store.count = 1; // 输出: Count: 1
// 清除 autorun
dispose();
// 不再响应
store.count = 2; // 没有输出
选项配置 #
delay选项 #
设置延迟执行(防抖):
javascript
import { autorun } from 'mobx';
const dispose = autorun(
() => {
console.log(`Count: ${store.count}`);
},
{ delay: 300 } // 延迟 300ms 执行
);
// 快速修改多次,只执行一次
store.count = 1;
store.count = 2;
store.count = 3;
// 300ms 后只输出: Count: 3
name选项 #
设置名称便于调试:
javascript
autorun(
() => {
console.log(store.count);
},
{ name: 'countLogger' }
);
scheduler选项 #
自定义调度器:
javascript
autorun(
() => {
console.log(store.count);
},
{
scheduler: (run) => {
// 使用 requestAnimationFrame 调度
requestAnimationFrame(run);
}
}
);
使用场景 #
日志记录 #
javascript
import { autorun, toJS } from 'mobx';
// 记录状态变化
autorun(() => {
console.log('State changed:', toJS(store));
});
本地存储同步 #
javascript
import { autorun, toJS } from 'mobx';
// 自动保存到 localStorage
autorun(() => {
localStorage.setItem('user', JSON.stringify(toJS(store.user)));
});
// 自动保存到 sessionStorage
autorun(() => {
sessionStorage.setItem('token', store.token);
});
DOM更新 #
javascript
import { autorun } from 'mobx';
// 更新文档标题
autorun(() => {
document.title = `${store.unreadCount} unread messages`;
});
// 更新 DOM 元素
autorun(() => {
const element = document.getElementById('counter');
if (element) {
element.textContent = store.count;
}
});
网络请求 #
javascript
import { autorun, runInAction } from 'mobx';
// 自动搜索
let searchTimeout;
autorun(() => {
if (searchTimeout) clearTimeout(searchTimeout);
searchTimeout = setTimeout(async () => {
if (store.searchQuery) {
const results = await fetch(`/api/search?q=${store.searchQuery}`);
runInAction(() => {
store.searchResults = await results.json();
});
}
}, 300);
});
完整示例 #
实时通知系统 #
javascript
import { makeAutoObservable, autorun } from 'mobx';
class NotificationStore {
notifications = [];
unreadCount = 0;
constructor() {
makeAutoObservable(this);
// 自动更新未读计数
autorun(() => {
this.unreadCount = this.notifications.filter(n => !n.read).length;
});
// 自动更新文档标题
autorun(() => {
if (this.unreadCount > 0) {
document.title = `(${this.unreadCount}) New Notifications`;
} else {
document.title = 'App';
}
});
// 自动显示桌面通知
autorun(() => {
const unread = this.notifications.filter(n => !n.read && !n.notified);
unread.forEach(notification => {
this.showDesktopNotification(notification);
notification.notified = true;
});
});
}
showDesktopNotification(notification) {
if (Notification.permission === 'granted') {
new Notification(notification.title, {
body: notification.message
});
}
}
addNotification(notification) {
this.notifications.push({
id: Date.now(),
...notification,
read: false,
notified: false,
createdAt: new Date()
});
}
markAsRead(id) {
const notification = this.notifications.find(n => n.id === id);
if (notification) {
notification.read = true;
}
}
markAllAsRead() {
this.notifications.forEach(n => n.read = true);
}
clearAll() {
this.notifications = [];
}
}
export default new NotificationStore();
实时数据同步 #
javascript
import { makeAutoObservable, autorun, toJS } from 'mobx';
class SyncStore {
data = {};
lastSync = null;
syncEnabled = true;
constructor() {
makeAutoObservable(this);
// 自动同步到服务器
let syncTimeout;
autorun(() => {
if (!this.syncEnabled) return;
if (syncTimeout) clearTimeout(syncTimeout);
syncTimeout = setTimeout(async () => {
await this.syncToServer();
}, 1000);
});
}
async syncToServer() {
try {
const response = await fetch('/api/sync', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
data: toJS(this.data),
lastSync: this.lastSync
})
});
if (response.ok) {
this.lastSync = new Date();
}
} catch (error) {
console.error('Sync failed:', error);
}
}
updateData(key, value) {
this.data[key] = value;
}
enableSync() {
this.syncEnabled = true;
}
disableSync() {
this.syncEnabled = false;
}
}
export default new SyncStore();
表单自动保存 #
javascript
import { makeAutoObservable, autorun, toJS } from 'mobx';
class FormStore {
values = {};
errors = {};
isDirty = false;
lastSaved = null;
constructor() {
makeAutoObservable(this);
// 自动保存
let saveTimeout;
autorun(() => {
if (!this.isDirty) return;
if (saveTimeout) clearTimeout(saveTimeout);
saveTimeout = setTimeout(async () => {
await this.save();
}, 2000);
});
}
async save() {
try {
const response = await fetch('/api/form', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(toJS(this.values))
});
if (response.ok) {
this.isDirty = false;
this.lastSaved = new Date();
console.log('Form saved');
}
} catch (error) {
console.error('Save failed:', error);
}
}
setField(name, value) {
this.values[name] = value;
this.isDirty = true;
}
reset() {
this.values = {};
this.errors = {};
this.isDirty = false;
}
}
export default new FormStore();
注意事项 #
1. 避免循环依赖 #
javascript
// 错误:会导致无限循环
autorun(() => {
store.count++; // 修改了自己依赖的状态
console.log(store.count);
});
// 正确:只读取状态
autorun(() => {
console.log(store.count);
});
2. 异步操作 #
javascript
// 错误:异步代码不会被追踪
autorun(async () => {
const data = await fetchData();
console.log(data.count); // 不会被追踪
});
// 正确:使用 runInAction
autorun(async () => {
const data = await fetchData();
runInAction(() => {
console.log(data.count);
});
});
3. 条件访问 #
javascript
// 只追踪实际访问的状态
autorun(() => {
if (store.isLoggedIn) {
console.log(store.user.name); // 只在登录时追踪 user.name
}
});
autorun与其他Reaction对比 #
| 特性 | autorun | reaction | when |
|---|---|---|---|
| 立即执行 | 是 | 否 | 否 |
| 条件触发 | 否 | 是 | 是 |
| 执行次数 | 多次 | 多次 | 一次 |
| 返回值 | 清除函数 | 清除函数 | Promise |
总结 #
autorun 的核心要点:
- 立即执行:创建时立即执行一次
- 自动追踪:自动收集依赖
- 自动响应:依赖变化时自动重新执行
- 需要清除:不使用时要调用清除函数
使用场景:
- 日志记录
- 本地存储同步
- DOM 更新
- 网络请求
- 实时同步
继续学习 Reaction反应,了解更精确的响应式控制。
最后更新:2026-03-28