MobX 6 相比 MobX 4/5 有哪些重要变化?
MobX 6 是 MobX 的最新主要版本,与 MobX 4/5 相比有多个破坏性变更和 API 调整。理解这些变化对于项目升级至关重要。
核心变化:装饰器默认移除,改用 makeObservable
MobX 6 默认不再支持装饰器语法,引入 makeObservable 和 makeAutoObservable 替代。
MobX 4/5(装饰器写法):
javascriptimport { observable, action, computed } from 'mobx'; class TodoStore { @observable todos = []; @observable filter = 'all'; @computed get completedTodos() { return this.todos.filter(todo => todo.completed); } @action addTodo(text) { this.todos.push({ text, completed: false }); } }
MobX 6(推荐写法):
javascriptimport { makeAutoObservable } from 'mobx'; class TodoStore { todos = []; filter = 'all'; constructor() { makeAutoObservable(this); } get completedTodos() { return this.todos.filter(todo => todo.completed); } addTodo(text) { this.todos.push({ text, completed: false }); } }
makeAutoObservable 自动推断属性类型:getter → computed、方法 → action、其余 → observable。需要精细控制时用 makeObservable,显式标注每个成员:
javascriptimport { makeObservable, observable, action, computed } from 'mobx'; class TodoStore { todos = []; filter = 'all'; constructor() { makeObservable(this, { todos: observable, filter: observable, completedTodos: computed, addTodo: action.bound, // 自动绑定 this }); } get completedTodos() { return this.todos.filter(todo => todo.completed); } addTodo(text) { this.todos.push({ text, completed: false }); } }
关键区别: makeAutoObservable 不能用于子类(超类和子类都引入 observable 成员时,必须各自调用 makeObservable)。action.bound 只能在 makeObservable 中使用。
configure 仍在,默认行为变更
原文有误:MobX 6 并未移除 configure API,而是调整了默认值。
javascriptimport { configure } from 'mobx'; // MobX 6 的 configure 仍然可用 configure({ enforceActions: 'always', // 默认值改为 'observed' computedRequiresReaction: true, // 新增 lint 选项 reactionRequiresObservable: true, // 新增 lint 选项 observableRequiresReaction: true, // 新增 lint 选项 useProxies: 'never', // 可禁用 Proxy });
主要变化:
enforceActions默认值从'never'改为'observed',即被观察的状态必须通过 action 修改- 新增多个 lint 选项帮助捕获常见错误
useProxies可设为'never'兼容不支持 Proxy 的环境(如旧版 React Native)
Proxy 成为默认机制
MobX 6 默认使用 Proxy 实现可观察对象,这意味着:
- 数组和普通对象的属性添加/删除会被自动追踪
- 不再需要
extendObservable来添加新属性
javascriptconst store = makeAutoObservable({ user: null, }); // MobX 5: 新属性不会触发响应 // MobX 6: Proxy 自动追踪,以下操作是响应式的 store.user = { name: 'Alice' }; // 自动变为 observable
如果环境不支持 Proxy,需要配置 useProxies: 'never',此时行为退回 MobX 5 模式,动态添加属性需使用 observable.set() 工具函数。
extras 拆分到主 API
extras 命名空间下的工具函数被提升到顶层导出:
javascript// MobX 4/5 import { extras } from 'mobx'; extras.isObservable(obj); extras.getAtom(obs); // MobX 6 import { isObservable, getAtom } from 'mobx'; isObservable(obj); getAtom(obs);
intercept 和 observe 移除
intercept 和 observe 在 MobX 6 中被移除,用 reaction / autorun 替代:
javascript// MobX 4/5 import { observe } from 'mobx'; observe(store.todos, (change) => { console.log('Changed:', change); }); // MobX 6 import { reaction } from 'mobx'; reaction( () => [...store.todos], // 追踪整个数组快照 (todos, prevTodos) => { console.log('Todos changed'); } );
如果需要拦截修改,使用 action 包装修改逻辑。
React 集成:弃用 inject/Provider
MobX 6 推荐使用 React Context 替代 mobx-react 的 inject 和 Provider:
javascriptimport { observer } from 'mobx-react-lite'; import { createContext, useContext } from 'react'; const StoreContext = createContext(null); const useStore = () => { const store = useContext(StoreContext); if (!store) throw new Error('useStore must be within StoreProvider'); return store; }; // 函数组件 + observer const TodoList = observer(() => { const store = useStore(); return <div>{store.completedTodos.length} completed</div>; }); // 根组件 function App() { return ( <StoreContext.Provider value={todoStore}> <TodoList /> </StoreContext.Provider> ); }
注意: mobx-react-lite 只支持函数组件。如果项目仍有类组件,继续使用 mobx-react 的 observer HOC,但不再使用 inject。
TypeScript 支持改进
MobX 6 对 TypeScript 类型推断更完善:
typescriptclass Store { items: Item[] = []; filter: 'all' | 'active' | 'completed' = 'all'; constructor() { // 泛型参数确保类型推断正确 makeAutoObservable<Store>(this, { items: observable.shallow, // 浅层观察,适合数组只关心引用变化 }); } get filteredItems(): Item[] { return this.items.filter(i => i.status === this.filter); } }
observable.shallow 是 MobX 6 新增的修饰器,对集合只做浅层响应式转换,避免深层对象都被 proxy 包装,适合存储不可变数据。
迁移实战要点
1. 装饰器迁移(最关键)
每个使用装饰器的类,都需要在 constructor 中添加 makeObservable(this),或改为 makeAutoObservable(this)。可使用官方 mobx-undecorate codemod 自动迁移:
bashnpx mobx-undecorate
2. 视图不刷新的排查
升级后组件不更新,通常是忘记调用 makeObservable(this) 或 makeAutoObservable(this)。MobX 6 要求每个有 observable 成员的类都在构造函数中调用。
3. configure 兼容
检查项目中所有 configure 调用,确认选项是否需要调整。enforceActions 默认值变为 'observed',可能触发新的警告。
4. observable 动态属性
MobX 6 使用 Proxy 后,直接赋值新属性会自动变为 observable。但如果禁用了 Proxy,需要用工具函数:
javascriptimport { set, remove } from 'mobx'; // 禁用 Proxy 时添加/删除属性 set(store, 'newProp', value); remove(store, 'newProp');
5. 统一版本
MobX 6 合并了 MobX 4(ES5)和 MobX 5(Proxy)两条分支,现在一个包同时支持两种模式,根据 useProxies 配置自动切换。
常见追问
Q: 能否继续使用装饰器?
可以。MobX 6 仍支持旧版装饰器(需 Babel/TS 配置),但将在下个大版本移除。推荐使用 TC39 Stage 3 新装饰器语法 @observable accessor:
javascriptclass Store { @observable accessor count = 0; // 新装饰器语法 }
Q: makeAutoObservable 和 makeObservable 怎么选?
简单 Store 用 makeAutoObservable,代码更简洁。需要 action.bound、observable.shallow、子类继承或排除某些属性时,用 makeObservable 显式标注。
Q: 升级后性能会变差吗?
不会。Proxy 机制反而比 MobX 5 的 getter/setter 劫持更高效。包体积通过 tree-shaking 也更小。如需极致性能,observable.shallow 可减少深层 proxy 开销。