MobX 深度解析

MobX 深度解析

一、核心概念

1. Observable(观察状态)

observable观察,可以使用observable包裹类来实现,创建响应式数据(类似Vue的reactive),
不过目前常用的方法是makeObservable和makeAutoObservable,会自动把当前class的自由对象为observable,get方法为computed,set方法为action 。
创建响应式数据的方法(类似 Vue 的 reactive):

// 方法一:使用 observable 包裹
import { observable } from 'mobx';
const user = observable({ name: 'John', age: 30 });

// 方法二:类中使用装饰器(常用)
import { makeObservable, observable, action, computed } from 'mobx';

class UserStore {
  name = 'John';
  age = 30;
  
  constructor() {
    makeObservable(this, {
      name: observable,
      age: observable,
      // 其他配置...
    });
  }
}

// 方法三:自动观察(推荐)
import { makeAutoObservable } from 'mobx';

class UserStore {
  name = 'John';
  age = 30;
  
  constructor() {
    makeAutoObservable(this);
    // 自动将:
    // - 属性标记为 observable
    // - getter 标记为 computed
    // - 方法标记为 action
  }
  
  get isAdult() {
    return this.age >= 18; // 自动成为 computed
  }
  
  setName(name) {
    this.name = name; // 自动成为 action
  }
}

2. Action(状态变更)

action的用法,只有是类的字段的函数方法会被makeAutoObservable自动包装为action,
如果是在异步回调如promise中要显式的action包装,使用action或 runInAction方法,或者使用flow (generator),即*标注为生成器函数,用yield替代await。
核心原则:所有状态变更都应该在 action 中执行。

import { action, runInAction, flow } from 'mobx';

class UserStore {
  // 同步 action(自动标记)
  updateName(name) {
    this.name = name;
  }
  
  // 异步 action 方案一:runInAction
  async fetchUser() {
    const response = await fetch('/api/user');
    runInAction(() => {
      this.user = response.data;
    });
  }
  
  // 异步 action 方案二:flow(生成器函数)
  *fetchUserFlow() {
    const response = yield fetch('/api/user');
    this.user = response.data; // 自动在 action 中
  }
}

// 强制使用 action 修改状态(推荐)
import { configure } from 'mobx';
configure({ enforceActions: 'always' });

3. Reactions(反应)

reactions,可以看作是useeffect副作用的概念,更像是vue中的watch,computed也类似vue的computed。
autorun可以自动追踪依赖变化,类似vue的WatchEffect;也可以使用reaction或者when方法,类似vue的watch,需要显式的声明监听依赖对象。
When方法的特点是,满足特定条件时执行一次,执行后自动清理,后续不会再触发。

类似于 Vue 的 watchcomputed

import { autorun, reaction, when, computed } from 'mobx';

// 1. autorun:自动追踪依赖(类似 Vue 的 watchEffect)
const disposer = autorun(() => {
  console.log(`User: ${user.name}, Age: ${user.age}`);
});

// 2. reaction:显式声明依赖(类似 Vue 的 watch)
reaction(
  () => user.age, // 跟踪的表达式
  (age, previousAge) => {
    console.log(`Age changed from ${previousAge} to ${age}`);
  }
);

// 3. when:条件执行一次
when(
  () => user.age >= 18, // 条件
  () => {
    console.log('User is now an adult');
  } // 执行后自动清理
);

// 4. computed:计算属性
class UserStore {
  get displayName() {
    return `${this.name} (${this.age})`;
  }
}

4. 在 React 中集成 MobX

在react中集成mobx,一般会选择observer观察某个需要响应式更新的组件,以此实现类似于vue中默认的响应式组件。
需要注意的是,observer包裹的组件,其中的观察对象或者说属性,需要是一个observable,所以经常会使用useLocalObservable来创建一个局部的响应式对象。
要把一个observable赋值给react组件时,必须将其转换回js对象,一般使用toJS方法。
在useEffect 中,组件销毁时记得清理autorun/reaction,使用disposer(),when执行后会自动清理,但如果未执行也应清理,使用whenDisposer。

import { observer, useLocalObservable } from 'mobx-react-lite';
import { toJS } from 'mobx';
import { useEffect } from 'react';

// 1. 使用 observer 包装组件
const UserComponent = observer(({ userStore }) => {
  // 2. 创建局部响应式对象
  const localState = useLocalObservable(() => ({
    count: 0,
    increment() {
      this.count++;
    }
  }));
  
  // 3. 转换 observable 为普通 JS 对象
  const userData = toJS(userStore.user);
  
  // 4. 在 useEffect 中处理 reactions
  useEffect(() => {
    const disposer = autorun(() => {
      console.log('User changed:', userStore.name);
    });
    
    // 清理函数
    return () => disposer();
  }, [userStore]);
  
  // 5. when 的清理
  useEffect(() => {
    const whenDisposer = when(
      () => userStore.age >= 18,
      () => console.log('Became adult')
    );
    
    return () => whenDisposer(); // 如果 when 未执行,需要手动清理
  }, [userStore]);
  
  return <div>{userStore.name}</div>;
});

二、最佳实践

1. 状态管理模式

// Store 模式
class RootStore {
  constructor() {
    this.userStore = new UserStore(this);
    this.uiStore = new UIStore(this);
  }
}

// 使用 Context 提供 Store
const StoreContext = React.createContext();
const useStore = () => useContext(StoreContext);

2. 性能优化

// 1. 使用 observer 精细控制
const UserList = observer(({ users }) => (
  <div>
    {users.map(user => (
      <UserItem key={user.id} user={user} />
    ))}
  </div>
));

// 2. 避免不必要的重新渲染
const UserItem = observer(({ user }) => (
  <div>{user.name}</div>
));

// 3. 批量更新
import { runInAction } from 'mobx';
const updateMultiple = () => {
  runInAction(() => {
    store.user.name = 'New Name';
    store.user.age = 31;
  });
};

3. 调试技巧

import { trace } from 'mobx';

// 跟踪 computed/action 的执行
class UserStore {
  get displayName() {
    trace(); // 在控制台显示依赖追踪
    return `${this.name} (${this.age})`;
  }
}

// 配置开发工具
import { configure } from 'mobx';
configure({
  enforceActions: 'always',
  computedRequiresReaction: true,
  reactionRequiresObservable: true,
  observableRequiresReaction: false,
  disableErrorBoundaries: false
});

三、与 Vue 的对比

特性 MobX + React Vue
响应式基础 显式使用 observable 默认响应式
状态变更 需要 action 包装 直接赋值
计算属性 computed getter computed 函数
副作用 autorun/reaction/when watch/watchEffect
组件更新 需要 observer 包装 默认自动更新
异步处理 runInAction/flow 直接赋值,自动追踪

四、总结

MobX + React ≈ Vue 的自动响应式能力

MobX 为 React 提供了类似 Vue 的自动响应式系统:

  • 声明式状态:使用 observable 声明响应式数据
  • 自动追踪:依赖自动收集,变更自动触发更新
  • 细粒度更新:只有真正变化的组件重新渲染
  • 简单直观:相比 Redux 减少了模板代码

适用场景

  • 需要快速开发的中大型应用
  • 团队熟悉面向对象编程
  • 希望获得 Vue 式开发体验的 React 项目

注意事项

  1. 始终在 action 中修改状态
  2. 使用 observer 包装需要响应式更新的组件
  3. 及时清理 reactions 避免内存泄漏
  4. 考虑使用 makeAutoObservable 简化代码
posted @ 2025-12-26 15:39  Allis  阅读(2)  评论(0)    收藏  举报