乐闻世界logo
搜索文章和话题

What are the best practices for MobX performance optimization?

2月21日 15:49

MobX is already a high-performance state management library, but in real applications, there are still some optimization techniques that can further improve performance. Here are the best practices for MobX performance optimization:

1. Use computed Reasonably

computed's Caching Mechanism

computed properties automatically cache results and only recalculate when dependencies change:

javascript
class 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`; } } // First access will compute console.log(store.info); // Computing fullName, Computing info // Second access uses cache console.log(store.info); // No output // Modify age, only info recalculates store.age = 31; console.log(store.info); // Computing info

Avoid Side Effects in computed

javascript
// Wrong: Side effects in computed @computed get badComputed() { console.log('Side effect!'); // Shouldn't be in computed fetch('/api/data'); // Shouldn't be in computed return this.data; } // Correct: computed should be pure functions @computed get goodComputed() { return this.data.filter(item => item.active); }

2. Optimize observable Usage

Only Use observable for State That Needs Tracking

javascript
// Bad practice: All state is observable class Store { @observable config = { apiUrl: 'https://api.example.com', timeout: 5000, retries: 3 }; } // Good practice: Only use observable for state that changes class Store { config = { apiUrl: 'https://api.example.com', timeout: 5000, retries: 3 }; @observable data = []; @observable loading = false; }

Use shallow or deep to Control Observable Depth

javascript
import { observable, deep, shallow } from 'mobx'; // Deep observable (default) const deepStore = observable({ user: { profile: { name: 'John' } } }); // Shallow observable const shallowStore = observable.shallow({ users: [ { name: 'John' }, { name: 'Jane' } ] }); // Only the array itself is observable, objects in the array are not

3. Batch State Updates

Use runInAction to Batch Updates

javascript
// Bad practice: Multiple updates trigger multiple times @action badUpdate() { this.count++; this.name = 'New Name'; this.age++; } // Good practice: Batch updates @action goodUpdate() { runInAction(() => { this.count++; this.name = 'New Name'; this.age++; }); }

Use transaction (MobX 4/5)

javascript
import { transaction } from 'mobx'; transaction(() => { store.count++; store.name = 'New Name'; store.age++; });

4. Optimize Component Rendering

Use observer Only Where Needed

javascript
// Bad practice: All components use observer @observer const Header = () => <h1>My App</h1>; @observer const Footer = () => <footer>© 2024</footer>; // Good practice: Only use observer on components that need to respond to state changes const Header = () => <h1>My App</h1>; const Footer = () => <footer>© 2024</footer>; @observer const Counter = () => <div>{store.count}</div>;

Split Components to Reduce Dependencies

javascript
// Bad practice: Component depends on too much state @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> ); }; // Good practice: Split into multiple components @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>; };

Use React.memo with observer

javascript
const PureComponent = React.memo(observer(() => { return <div>{store.count}</div>; }));

5. Avoid Creating New Objects in render

javascript
// Bad practice: Create new object on every render @observer const BadComponent = () => { const style = { color: 'red' }; const handleClick = () => console.log('clicked'); return <div style={style} onClick={handleClick}>{store.count}</div>; }; // Good practice: Define outside component const style = { color: 'red' }; const handleClick = () => console.log('clicked'); @observer const GoodComponent = () => { return <div style={style} onClick={handleClick}>{store.count}</div>; };

6. Use trace to Debug Performance Issues

javascript
import { trace } from 'mobx'; // Track dependencies of computed trace(store.fullName); // Track dependencies of reaction autorun(() => { console.log(store.count); }, { name: 'myReaction' }); // Track component rendering @observer class MyComponent extends React.Component { render() { trace(true); // Track component rendering return <div>{store.count}</div>; } }

7. Optimize with configure

javascript
import { configure } from 'mobx'; configure({ // Enforce all state modifications in actions enforceActions: 'always', // Use Proxy (if available) useProxies: 'ifavailable', // computed requires reaction to calculate computedRequiresReaction: false, // Disable unnecessary warnings isolateGlobalState: true });

8. Optimize Array Operations

Use splice Instead of Reassignment

javascript
// Bad practice: Reassign entire array @action badAddItem(item) { this.items = [...this.items, item]; } // Good practice: Use splice @action goodAddItem(item) { this.items.push(item); }

Use replace for Batch Replacement

javascript
@action replaceItems(newItems) { this.items.replace(newItems); }

9. Use reaction Instead of autorun

javascript
// Bad practice: autorun executes immediately autorun(() => { console.log(store.count); }); // Good practice: reaction provides finer control reaction( () => store.count, (count) => { console.log(count); }, { fireImmediately: false } );

10. Use when for One-time Conditions

javascript
// Bad practice: Use autorun for one-time condition autorun(() => { if (store.data.length > 0) { processData(store.data); } }); // Good practice: Use when when( () => store.data.length > 0, () => processData(store.data) );

11. Avoid Circular Dependencies

javascript
// Bad practice: Circular dependencies class StoreA { @observable value = 0; @computed get doubled() { return storeB.value * 2; } } class StoreB { @observable value = 0; @computed get doubled() { return storeA.value * 2; } } // Good practice: Avoid circular dependencies class Store { @observable valueA = 0; @observable valueB = 0; @computed get doubledA() { return this.valueA * 2; } @computed get doubledB() { return this.valueB * 2; } }

12. Clean Up Unnecessary Reactions

javascript
// Clean up reaction when component unmounts useEffect(() => { const dispose = autorun(() => { console.log(store.count); }); return () => { dispose(); // Clean up reaction }; }, []);

13. Use MobX DevTools to Analyze Performance

MobX DevTools provides powerful performance analysis features:

  • View dependency graphs
  • Monitor state changes
  • Analyze rendering performance
  • Debug computed and reaction

14. Avoid Over-tracking

javascript
// Bad practice: Access observable in loop @observer const BadComponent = () => { return ( <div> {store.items.map(item => ( <div key={item.id}> {item.name} - {item.value} </div> ))} </div> ); }; // Good practice: Use computed to preprocess data 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. Use makeAutoObservable to Simplify Code

javascript
// MobX 6 recommends using makeAutoObservable class Store { count = 0; firstName = 'John'; lastName = 'Doe'; constructor() { makeAutoObservable(this); } get fullName() { return `${this.firstName} ${this.lastName}`; } increment() { this.count++; } }

Summary

Key points for MobX performance optimization:

  1. Reasonably use computed's caching mechanism
  2. Only use observable for state that needs tracking
  3. Batch state updates to reduce trigger times
  4. Optimize component rendering, reduce unnecessary re-renders
  5. Avoid creating new objects in render
  6. Use trace to debug performance issues
  7. Clean up unnecessary reactions
  8. Avoid circular dependencies and over-tracking

Following these best practices can build high-performance MobX applications.

标签:Mobx