MobX 本身已经是一个高性能的状态管理库,但在实际应用中,仍然有一些优化技巧可以进一步提升性能。以下是 MobX 性能优化的最佳实践:
1. 合理使用 computed
computed 的缓存机制
computed 属性会自动缓存结果,只在依赖项变化时重新计算:
javascriptclass Store { @observable firstName = 'John'; @observable lastName = 'Doe'; @observable age = 30; @computed get fullName() { console.log('Computing fullName'); return `${this.firstName} ${this.lastName}`; } @computed get info() { console.log('Computing info'); return `${this.fullName}, ${this.age} years old`; } } // 第一次访问会计算 console.log(store.info); // Computing fullName, Computing info // 再次访问,使用缓存 console.log(store.info); // 无输出 // 修改 age,只重新计算 info store.age = 31; console.log(store.info); // Computing info
避免在 computed 中产生副作用
javascript// 错误:在 computed 中产生副作用 @computed get badComputed() { console.log('Side effect!'); // 不应该在 computed 中 fetch('/api/data'); // 不应该在 computed 中 return this.data; } // 正确:computed 应该是纯函数 @computed get goodComputed() { return this.data.filter(item => item.active); }
2. 优化 observable 的使用
只对需要追踪的状态使用 observable
javascript// 不好的做法:所有状态都是 observable class Store { @observable config = { apiUrl: 'https://api.example.com', timeout: 5000, retries: 3 }; } // 好的做法:只对会变化的状态使用 observable class Store { config = { apiUrl: 'https://api.example.com', timeout: 5000, retries: 3 }; @observable data = []; @observable loading = false; }
使用 shallow 或 deep 控制可观察深度
javascriptimport { observable, deep, shallow } from 'mobx'; // 深度可观察(默认) const deepStore = observable({ user: { profile: { name: 'John' } } }); // 浅层可观察 const shallowStore = observable.shallow({ users: [ { name: 'John' }, { name: 'Jane' } ] }); // 只有数组本身是可观察的,数组中的对象不是
3. 批量更新状态
使用 runInAction 批量更新
javascript// 不好的做法:多次触发更新 @action badUpdate() { this.count++; this.name = 'New Name'; this.age++; } // 好的做法:批量更新 @action goodUpdate() { runInAction(() => { this.count++; this.name = 'New Name'; this.age++; }); }
使用 transaction(MobX 4/5)
javascriptimport { transaction } from 'mobx'; transaction(() => { store.count++; store.name = 'New Name'; store.age++; });
4. 优化组件渲染
使用 observer 只在需要的地方
javascript// 不好的做法:所有组件都用 observer @observer const Header = () => <h1>My App</h1>; @observer const Footer = () => <footer>© 2024</footer>; // 好的做法:只在需要响应状态变化的组件上使用 observer const Header = () => <h1>My App</h1>; const Footer = () => <footer>© 2024</footer>; @observer const Counter = () => <div>{store.count}</div>;
拆分组件以减少依赖
javascript// 不好的做法:组件依赖太多状态 @observer const BadComponent = () => { return ( <div> <div>{store.user.name}</div> <div>{store.user.email}</div> <div>{store.settings.theme}</div> <div>{store.settings.language}</div> <div>{store.data.length}</div> </div> ); }; // 好的做法:拆分为多个组件 @observer const UserInfo = () => { return ( <div> <div>{store.user.name}</div> <div>{store.user.email}</div> </div> ); }; @observer const Settings = () => { return ( <div> <div>{store.settings.theme}</div> <div>{store.settings.language}</div> </div> ); }; @observer const DataCount = () => { return <div>{store.data.length}</div>; };
使用 React.memo 配合 observer
javascriptconst PureComponent = React.memo(observer(() => { return <div>{store.count}</div>; }));
5. 避免在 render 中创建新对象
javascript// 不好的做法:每次渲染都创建新对象 @observer const BadComponent = () => { const style = { color: 'red' }; const handleClick = () => console.log('clicked'); return <div style={style} onClick={handleClick}>{store.count}</div>; }; // 好的做法:在组件外部定义 const style = { color: 'red' }; const handleClick = () => console.log('clicked'); @observer const GoodComponent = () => { return <div style={style} onClick={handleClick}>{store.count}</div>; };
6. 使用 trace 调试性能问题
javascriptimport { trace } from 'mobx'; // 追踪 computed 的依赖 trace(store.fullName); // 追踪 reaction 的依赖 autorun(() => { console.log(store.count); }, { name: 'myReaction' }); // 追踪组件的渲染 @observer class MyComponent extends React.Component { render() { trace(true); // 追踪组件渲染 return <div>{store.count}</div>; } }
7. 使用 configure 优化配置
javascriptimport { configure } from 'mobx'; configure({ // 强制所有状态修改都在 action 中 enforceActions: 'always', // 使用 Proxy(如果可用) useProxies: 'ifavailable', // computed 需要 reaction 才能计算 computedRequiresReaction: false, // 禁用不需要的警告 isolateGlobalState: true });
8. 优化数组操作
使用 splice 而不是重新赋值
javascript// 不好的做法:重新赋值整个数组 @action badAddItem(item) { this.items = [...this.items, item]; } // 好的做法:使用 splice @action goodAddItem(item) { this.items.push(item); }
使用 replace 批量替换
javascript@action replaceItems(newItems) { this.items.replace(newItems); }
9. 使用 reaction 替代 autorun
javascript// 不好的做法:autorun 会立即执行 autorun(() => { console.log(store.count); }); // 好的做法:reaction 提供更细粒度的控制 reaction( () => store.count, (count) => { console.log(count); }, { fireImmediately: false } );
10. 使用 when 处理一次性条件
javascript// 不好的做法:使用 autorun 处理一次性条件 autorun(() => { if (store.data.length > 0) { processData(store.data); } }); // 好的做法:使用 when when( () => store.data.length > 0, () => processData(store.data) );
11. 避免循环依赖
javascript// 不好的做法:循环依赖 class StoreA { @observable value = 0; @computed get doubled() { return storeB.value * 2; } } class StoreB { @observable value = 0; @computed get doubled() { return storeA.value * 2; } } // 好的做法:避免循环依赖 class Store { @observable valueA = 0; @observable valueB = 0; @computed get doubledA() { return this.valueA * 2; } @computed get doubledB() { return this.valueB * 2; } }
12. 清理不需要的 reaction
javascript// 在组件卸载时清理 reaction useEffect(() => { const dispose = autorun(() => { console.log(store.count); }); return () => { dispose(); // 清理 reaction }; }, []);
13. 使用 MobX DevTools 分析性能
MobX DevTools 提供了强大的性能分析功能:
- 查看依赖关系图
- 监控状态变化
- 分析渲染性能
- 调试 computed 和 reaction
14. 避免过度追踪
javascript// 不好的做法:在循环中访问 observable @observer const BadComponent = () => { return ( <div> {store.items.map(item => ( <div key={item.id}> {item.name} - {item.value} </div> ))} </div> ); }; // 好的做法:使用 computed 预处理数据 class Store { @observable items = []; @computed get itemDisplayData() { return this.items.map(item => ({ id: item.id, display: `${item.name} - ${item.value}` })); } } @observer const GoodComponent = () => { return ( <div> {store.itemDisplayData.map(item => ( <div key={item.id}>{item.display}</div> ))} </div> ); };
15. 使用 makeAutoObservable 简化代码
javascript// MobX 6 推荐使用 makeAutoObservable class Store { count = 0; firstName = 'John'; lastName = 'Doe'; constructor() { makeAutoObservable(this); } get fullName() { return `${this.firstName} ${this.lastName}`; } increment() { this.count++; } }
总结
MobX 性能优化的关键点:
- 合理使用 computed 的缓存机制
- 只对需要追踪的状态使用 observable
- 批量更新状态减少触发次数
- 优化组件渲染,减少不必要的重新渲染
- 避免在 render 中创建新对象
- 使用 trace 调试性能问题
- 清理不需要的 reaction
- 避免循环依赖和过度追踪
遵循这些最佳实践,可以构建高性能的 MobX 应用。