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