在 MobX 中,observer 是一个高阶组件(HOC),用于将 React 组件转换为响应式组件。当组件使用的数据发生变化时,组件会自动重新渲染。
observer 的基本用法
1. 类组件中使用 observer
javascriptimport React from 'react'; import { observer } from 'mobx-react'; import { observable } from 'mobx'; class Store { @observable count = 0; } const store = new Store(); @observer class Counter extends React.Component { render() { return ( <div> <p>Count: {store.count}</p> <button onClick={() => store.count++}>Increment</button> </div> ); } }
2. 函数组件中使用 observer
javascriptimport React from 'react'; import { observer } from 'mobx-react-lite'; import { observable } from 'mobx'; class Store { @observable count = 0; } const store = new Store(); const Counter = observer(() => { return ( <div> <p>Count: {store.count}</p> <button onClick={() => store.count++}>Increment</button> </div> ); });
observer 的工作原理
1. 组件挂载时
- MobX 创建一个 reaction 来追踪组件 render 函数中访问的所有 observable
- 建立组件与 observable 之间的依赖关系
2. 状态变化时
- 当 observable 被修改时,MobX 检测到依赖变化
- 将组件标记为需要重新渲染
- 在下一个事件循环中,触发组件的重新渲染
3. 组件卸载时
- 自动清理 reaction 和依赖关系
- 避免内存泄漏
observer 的优化特性
1. 细粒度更新
observer 只会重新渲染真正需要更新的组件:
javascript@observer class Parent extends React.Component { render() { return ( <div> <ChildA /> <ChildB /> </div> ); } } @observer class ChildA extends React.Component { render() { // 只依赖 store.count return <div>Count: {store.count}</div>; } } @observer class ChildB extends React.Component { render() { // 只依赖 store.name return <div>Name: {store.name}</div>; } }
当 store.count 变化时,只有 ChildA 会重新渲染,ChildB 不会。
2. shouldComponentUpdate 优化
observer 会自动实现 shouldComponentUpdate,避免不必要的渲染:
- 只有当组件依赖的 observable 真正变化时才重新渲染
- 即使父组件重新渲染,子组件也可能不会重新渲染
3. 批量更新
多个状态变化会被批量处理,只触发一次重新渲染:
javascriptrunInAction(() => { store.count++; store.name = 'New Name'; });
observer 的最佳实践
1. 只在需要的地方使用 observer
不是所有组件都需要 observer,只在需要响应状态变化的组件上使用:
javascript// 不需要 observer const Header = () => <h1>My App</h1>; // 需要 observer const Counter = observer(() => { return <div>Count: {store.count}</div>; });
2. 避免在 render 中创建新对象
在 render 中创建新对象会导致不必要的重新渲染:
javascript// 不好的做法 const BadComponent = observer(() => { const style = { color: 'red' }; // 每次渲染都创建新对象 return <div style={style}>{store.count}</div>; }); // 好的做法 const style = { color: 'red' }; // 在组件外部定义 const GoodComponent = observer(() => { return <div style={style}>{store.count}</div>; });
3. 使用 computed 优化计算
在组件外部使用 computed 来优化计算逻辑:
javascript// 不好的做法 const BadComponent = observer(() => { const fullName = `${store.firstName} ${store.lastName}`; return <div>{fullName}</div>; }); // 好的做法 class Store { @observable firstName = 'John'; @observable lastName = 'Doe'; @computed get fullName() { return `${this.firstName} ${this.lastName}`; } } const GoodComponent = observer(() => { return <div>{store.fullName}</div>; });
4. 使用 React.memo 配合 observer
对于纯展示组件,可以结合 React.memo 使用:
javascriptconst PureComponent = React.memo(observer(() => { return <div>{store.count}</div>; }));
常见问题
1. 组件不更新
确保:
- 组件被 observer 包装
- 访问的是 observable 而不是普通对象
- 状态修改在 action 中进行
2. 过度渲染
如果组件过度渲染,检查:
- 是否在 render 中创建了新对象
- 是否使用了 computed 来优化计算
- 是否可以拆分组件以减少依赖
3. 内存泄漏
确保:
- 组件卸载时 observer 会自动清理
- 手动创建的 reaction 需要手动清理
总结
observer 是 MobX 与 React 集成的核心,它通过细粒度的依赖追踪实现了高效的响应式更新。正确使用 observer 和遵循最佳实践,可以构建高性能的 React 应用。