MobX 6 是 MobX 的最新主要版本,相比 MobX 4/5 有许多重要的变化和改进。了解这些变化对于升级和维护项目非常重要。
主要变化
1. 移除装饰器支持
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 }); } }
2. makeObservable 和 makeAutoObservable
MobX 6 引入了两个新的 API 来替代装饰器:
makeObservable
需要显式指定每个属性的类型。
javascriptimport { makeObservable, observable, action, computed } from 'mobx'; class TodoStore { todos = []; filter = 'all'; constructor() { makeObservable(this, { todos: observable, filter: observable, completedTodos: computed, addTodo: action }); } get completedTodos() { return this.todos.filter(todo => todo.completed); } addTodo(text) { this.todos.push({ text, completed: false }); } }
makeAutoObservable(推荐)
自动推断属性类型,更简洁。
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 }); } }
3. 移除 configure
MobX 6 移除了 configure API,不再需要全局配置。
MobX 4/5:
javascriptimport { configure } from 'mobx'; configure({ enforceActions: 'always', useProxies: 'ifavailable' });
MobX 6:
javascript// 不再需要 configure,默认行为已经优化
4. 移除 extras
MobX 6 移除了 extras API,相关功能被整合到主 API 中。
MobX 4/5:
javascriptimport { extras } from 'mobx'; const isObservable = extras.isObservable(obj);
MobX 6:
javascriptimport { isObservable } from 'mobx'; const isObservable = isObservable(obj);
5. 移除 intercept 和 observe
MobX 6 移除了 intercept 和 observe API,推荐使用 reaction 替代。
MobX 4/5:
javascriptimport { observe } from 'mobx'; const disposer = observe(store.todos, (change) => { console.log('Todo changed:', change); });
MobX 6:
javascriptimport { reaction } from 'mobx'; const disposer = reaction( () => store.todos.length, (length) => { console.log('Todo count changed:', length); } );
6. 类型推断改进
MobX 6 对 TypeScript 的支持更好,类型推断更准确。
typescriptimport { makeAutoObservable } from 'mobx'; class TodoStore { todos: Todo[] = []; filter: 'all' | 'completed' | 'active' = 'all'; constructor() { makeAutoObservable<TodoStore>(this); } get completedTodos(): Todo[] { return this.todos.filter(todo => todo.completed); } addTodo(text: string): void { this.todos.push({ text, completed: false }); } }
7. 移除 mobx-react 的 inject 和 Provider
MobX 6 推荐使用 React Context API,不再需要 inject 和 Provider。
MobX 4/5:
javascriptimport { Provider, inject, observer } from 'mobx-react'; @inject('todoStore') @observer class TodoList extends React.Component { render() { const { todoStore } = this.props; return <div>{/* ... */}</div>; } } function App() { return ( <Provider todoStore={todoStore}> <TodoList /> </Provider> ); }
MobX 6:
javascriptimport { observer } from 'mobx-react-lite'; import { createContext, useContext } from 'react'; const TodoContext = createContext(null); function TodoProvider({ children, store }) { return ( <TodoContext.Provider value={store}> {children} </TodoContext.Provider> ); } function useTodoStore() { const store = useContext(TodoContext); if (!store) { throw new Error('useTodoStore must be used within TodoProvider'); } return store; } const TodoList = observer(() => { const store = useTodoStore(); return <div>{/* ... */}</div>; }); function App() { return ( <TodoProvider store={todoStore}> <TodoList /> </TodoProvider> ); }
8. 性能改进
MobX 6 在性能方面有许多改进:
- 更小的包体积:通过 Tree-shaking 减少包体积
- 更快的响应速度:优化了依赖追踪算法
- 更好的内存管理:减少了内存占用
9. 错误处理改进
MobX 6 提供了更清晰的错误信息。
javascript// MobX 6 会提供更清晰的错误信息 class Store { data = []; constructor() { makeAutoObservable(this); } // 如果在 action 外修改状态 modifyData() { this.data.push({}); // 警告:在 action 外修改状态 } }
迁移指南
从 MobX 4/5 迁移到 MobX 6
1. 移除装饰器
之前:
javascriptclass Store { @observable data = []; @action addData(item) { this.data.push(item); } }
之后:
javascriptclass Store { data = []; constructor() { makeAutoObservable(this); } addData(item) { this.data.push(item); } }
2. 更新 mobx-react
之前:
javascriptimport { Provider, inject, observer } from 'mobx-react'; @inject('store') @observer class Component extends React.Component { render() { const { store } = this.props; return <div>{store.data}</div>; } }
之后:
javascriptimport { observer } from 'mobx-react-lite'; const Component = observer(() => { const store = useStore(); return <div>{store.data}</div>; });
3. 移除 configure
之前:
javascriptimport { configure } from 'mobx'; configure({ enforceActions: 'always' });
之后:
javascript// 不再需要 configure
4. 更新 extras
之前:
javascriptimport { extras } from 'mobx'; if (extras.isObservable(obj)) { // ... }
之后:
javascriptimport { isObservable } from 'mobx'; if (isObservable(obj)) { // ... }
最佳实践
1. 使用 makeAutoObservable
javascriptclass Store { data = []; constructor() { makeAutoObservable(this); } }
2. 使用 mobx-react-lite
javascriptimport { observer } from 'mobx-react-lite'; const Component = observer(() => { return <div>{/* ... */}</div>; });
3. 使用 React Context
javascriptconst StoreContext = createContext(null); function StoreProvider({ children, store }) { return ( <StoreContext.Provider value={store}> {children} </StoreContext.Provider> ); } function useStore() { const store = useContext(StoreContext); if (!store) { throw new Error('useStore must be used within StoreProvider'); } return store; }
4. 使用 TypeScript
typescriptclass Store { data: Data[] = []; constructor() { makeAutoObservable<Store>(this); } }
常见问题
1. 如何继续使用装饰器?
如果需要继续使用装饰器,可以安装 mobx-undecorate 包。
javascriptimport { decorate, observable, action } from 'mobx'; class Store { data = []; addData(item) { this.data.push(item); } } decorate(Store, { data: observable, addData: action });
2. 如何处理类型推断?
使用 makeAutoObservable 时,可以传入泛型参数。
typescriptclass Store { data: Data[] = []; constructor() { makeAutoObservable<Store>(this); } }
3. 如何处理 action.bound?
使用 action.bound 时,需要在 makeObservable 中指定。
javascriptclass Store { data = []; constructor() { makeObservable(this, { data: observable, addData: action.bound }); } addData(item) { this.data.push(item); } }
总结
- MobX 6 移除了装饰器支持,推荐使用
makeAutoObservable - 移除了
configure、extras、intercept、observe等 API - 推荐使用
mobx-react-lite和 React Context - 对 TypeScript 的支持更好
- 性能和错误处理都有改进
- 迁移相对简单,主要是替换装饰器语法