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

MobX 中的 makeObservable、makeAutoObservable 和装饰器有什么区别?

2月21日 15:50

MobX 提供了多种工具来创建和管理可观察状态,包括 makeObservablemakeAutoObservable 和装饰器。理解它们的区别和使用场景对于正确使用 MobX 至关重要。

1. makeObservable

基本用法

javascript
import { makeObservable, observable, computed, action } from 'mobx'; class Store { count = 0; firstName = 'John'; lastName = 'Doe'; constructor() { makeObservable(this, { count: observable, firstName: observable, lastName: observable, fullName: computed, increment: action, decrement: action.bound }); } get fullName() { return `${this.firstName} ${this.lastName}`; } increment() { this.count++; } decrement = () => { this.count--; }; }

特点

  • 显式声明:需要显式声明每个属性的类型
  • 灵活性高:可以精确控制每个属性的行为
  • 类型安全:与 TypeScript 集成良好
  • 需要配置:需要在构造函数中调用

适用场景

  • 需要精确控制每个属性的行为
  • 使用 TypeScript
  • 需要自定义配置

高级用法

javascript
class Store { data = []; loading = false; error = null; constructor() { makeObservable(this, { data: observable, loading: observable, error: observable, itemCount: computed, fetchData: action, clearData: action }, { autoBind: true }); // 自动绑定 this } get itemCount() { return this.data.length; } async fetchData() { this.loading = true; try { const response = await fetch('/api/data'); this.data = await response.json(); } catch (error) { this.error = error.message; } finally { this.loading = false; } } clearData() { this.data = []; this.error = null; } }

2. makeAutoObservable

基本用法

javascript
import { makeAutoObservable } from 'mobx'; class Store { count = 0; firstName = 'John'; lastName = 'Doe'; constructor() { makeAutoObservable(this); } get fullName() { return `${this.firstName} ${this.lastName}`; } increment() { this.count++; } decrement = () => { this.count--; }; }

特点

  • 自动推断:自动推断属性的类型
  • 简洁:代码更简洁,减少样板代码
  • 智能推断
    • getter → computed
    • 方法 → action
    • 字段 → observable
  • 可覆盖:可以覆盖默认推断

适用场景

  • 快速开发
  • 不需要精确控制
  • 代码简洁性优先

高级用法

javascript
class Store { data = []; loading = false; error = null; _internalState = {}; // 以下划线开头的属性不会被自动推断 constructor() { makeAutoObservable(this, { // 覆盖默认推断 data: observable.deep, fetchData: flow, _internalState: false // 不使其可观察 }); } get itemCount() { return this.data.length; } fetchData = flow(function* () { this.loading = true; try { const response = yield fetch('/api/data'); this.data = yield response.json(); } catch (error) { this.error = error.message; } finally { this.loading = false; } }); }

3. 装饰器

基本用法

javascript
import { observable, computed, action } from 'mobx'; class Store { @observable count = 0; @observable firstName = 'John'; @observable lastName = 'Doe'; @computed get fullName() { return `${this.firstName} ${this.lastName}`; } @action increment() { this.count++; } @action.bound decrement = () => { this.count--; }; }

特点

  • 声明式:使用装饰器语法
  • 简洁:代码更易读
  • 需要配置:需要 Babel 或 TypeScript 支持
  • MobX 6 中可选:装饰器不再是必需的

适用场景

  • 项目已配置装饰器支持
  • 喜欢装饰器语法
  • 需要与 MobX 4/5 兼容

高级用法

javascript
import { observable, computed, action, flow } from 'mobx'; class Store { @observable data = []; @observable loading = false; @observable error = null; @computed get itemCount() { return this.data.length; } @action async fetchData() { this.loading = true; try { const response = await fetch('/api/data'); this.data = await response.json(); } catch (error) { this.error = error.message; } finally { this.loading = false; } } @action.bound clearData() { this.data = []; this.error = null; } }

三者的对比

特性makeObservablemakeAutoObservable装饰器
声明方式显式配置自动推断装饰器
代码量较多
灵活性
TypeScript 支持
配置要求需要不需要需要 Babel/TS
MobX 6 推荐可选

选择指南

使用 makeObservable 当:

  • 需要精确控制每个属性的行为
  • 使用 TypeScript
  • 需要自定义配置
  • 需要覆盖默认行为
javascript
class Store { data = []; constructor() { makeObservable(this, { data: observable.shallow, // 浅层可观察 itemCount: computed, fetchData: action }); } }

使用 makeAutoObservable 当:

  • 快速开发
  • 不需要精确控制
  • 代码简洁性优先
  • 使用 MobX 6
javascript
class Store { data = []; constructor() { makeAutoObservable(this); } }

使用装饰器当:

  • 项目已配置装饰器支持
  • 喜欢装饰器语法
  • 需要与 MobX 4/5 兼容
javascript
class Store { @observable data = []; }

与 TypeScript 的集成

makeObservable + TypeScript

typescript
import { makeObservable, observable, computed, action } from 'mobx'; class Store { count: number = 0; firstName: string = 'John'; lastName: string = 'Doe'; constructor() { makeObservable<Store>(this, { count: observable, firstName: observable, lastName: observable, fullName: computed, increment: action }); } get fullName(): string { return `${this.firstName} ${this.lastName}`; } increment(): void { this.count++; } }

makeAutoObservable + TypeScript

typescript
import { makeAutoObservable } from 'mobx'; class Store { count: number = 0; firstName: string = 'John'; lastName: string = 'Doe'; constructor() { makeAutoObservable(this); } get fullName(): string { return `${this.firstName} ${this.lastName}`; } increment(): void { this.count++; } }

装饰器 + TypeScript

typescript
import { observable, computed, action } from 'mobx'; class Store { @observable count: number = 0; @observable firstName: string = 'John'; @observable lastName: string = 'Doe'; @computed get fullName(): string { return `${this.firstName} ${this.lastName}`; } @action increment(): void { this.count++; } }

最佳实践

1. MobX 6 推荐使用 makeAutoObservable

javascript
// 推荐 class Store { count = 0; constructor() { makeAutoObservable(this); } } // 也可以使用 makeObservable class Store { count = 0; constructor() { makeObservable(this, { count: observable }); } }

2. 使用 makeObservable 覆盖默认行为

javascript
class Store { data = []; constructor() { makeAutoObservable(this, { data: observable.shallow // 覆盖默认的深度可观察 }); } }

3. 使用 action.bound 或 autoBind

javascript
class Store { count = 0; constructor() { makeAutoObservable(this, {}, { autoBind: true }); } increment() { this.count++; // this 自动绑定 } }

4. 私有属性处理

javascript
class Store { data = []; _privateData = []; // 以下划线开头,不会被自动推断 constructor() { makeAutoObservable(this, { _privateData: false // 明确不使其可观察 }); } }

常见问题

1. 装饰器不工作

确保:

  • 配置了 Babel 或 TypeScript 装饰器支持
  • 使用了正确的装饰器语法
  • MobX 版本支持装饰器

2. makeAutoObservable 推断错误

javascript
// 如果推断错误,使用 makeObservable 显式声明 class Store { data = []; constructor() { makeAutoObservable(this, { data: observable.shallow // 显式声明 }); } }

3. TypeScript 类型错误

javascript
// 使用泛型参数 class Store { count = 0; constructor() { makeObservable<Store>(this, { count: observable }); } }

总结

在 MobX 6 中,推荐使用 makeAutoObservable 进行快速开发,使用 makeObservable 进行精确控制。装饰器仍然可用,但不再是必需的。选择哪种方式取决于项目需求和个人偏好。

标签:Mobx