装饰器
启用装饰器
经过多年的修改,ES 装饰器终于在 TC39 进程中达到了第 3 阶段,这意味着它们非常稳定,并且不会像之前的装饰器提案那样再次发生重大变化。MobX 已经实现了对这种新的“2022.3/第 3 阶段”装饰器语法的支持。使用现代装饰器,不再需要调用 `makeObservable` / `makeAutoObservable`。
2022.3 装饰器在以下方面得到支持
- TypeScript(5.0 及更高版本,确保 `experimentalDecorators` 标志未启用)。示例提交。
- 对于 Babel,请确保插件
proposal-decorators
启用并使用最高版本(目前为 `2023-05`)。示例提交。
// tsconfig.json
{
"compilerOptions": {
"experimentalDecorators": false /* or just remove the flag */
}
}
// babel.config.json (or equivalent)
{
"plugins": [
[
"@babel/plugin-proposal-decorators",
{
"version": "2023-05"
}
]
]
}
使用装饰器
import { observable, computed, action } from "mobx"
class Todo {
id = Math.random()
@observable accessor title = ""
@observable accessor finished = false
@action
toggle() {
this.finished = !this.finished
}
}
class TodoList {
@observable accessor todos = []
@computed
get unfinishedTodoCount() {
return this.todos.filter(todo => !todo.finished).length
}
}
请注意在使用 `@observable` 时使用新的 `accessor` 关键字。它是 2022.3 规范的一部分,如果您想使用现代装饰器,则需要它。
使用传统装饰器
我们不建议代码库使用 TypeScript / Babel 传统装饰器,因为它们永远不会成为语言的正式部分,但您仍然可以使用它们。这需要对转译进行特定的设置。
MobX 在 6.0 版本之前鼓励使用传统装饰器,并将事物标记为 `observable`、`computed` 和 `action`。虽然 MobX 6 建议不要使用这些装饰器(而是使用现代装饰器或 makeObservable
/ makeAutoObservable
),但在当前主版本中仍然可以使用。在 MobX 7 中将删除对传统装饰器的支持。
import { makeObservable, observable, computed, action } from "mobx"
class Todo {
id = Math.random()
@observable title = ""
@observable finished = false
constructor() {
makeObservable(this)
}
@action
toggle() {
this.finished = !this.finished
}
}
class TodoList {
@observable todos = []
@computed
get unfinishedTodoCount() {
return this.todos.filter(todo => !todo.finished).length
}
constructor() {
makeObservable(this)
}
}
从传统装饰器迁移
要从传统装饰器迁移到现代装饰器,请执行以下步骤。
- 禁用/删除 TypeScript 配置(或 Babel 等效项)中的 `experimentalDecorators` 标志。
- 从使用装饰器的类构造函数中删除所有 `makeObservable(this)` 调用。
- 将所有 `@observable`(及其变体)的实例替换为 `@observable accessor`。
装饰器更改/注意事项
MobX 的 2022.3 装饰器与 MobX 5 装饰器非常相似,因此用法基本相同,但有一些注意事项。
- `@observable accessor` 装饰器不可枚举。`accessor` 在过去没有直接等效项 - 它们是语言中的一个新概念。我们选择将它们设为不可枚举的、非自身属性,以便更好地遵循 ES 语言的精神以及 `accessor` 的含义。枚举性的主要案例似乎围绕序列化和剩余解构。
- 关于序列化,隐式序列化所有属性可能在 OOP 世界中并不理想,因此这似乎不是一个重大问题(考虑实现 `toJSON` 或使用 `serializr` 作为可能的替代方案)。
- 解决剩余解构问题,这种方法在 MobX 中是一种反模式 - 这样做会(可能不必要地)触及所有可观察对象并使观察者过度反应)。
- `@action some_field = () => {}` 过去和现在都是有效的用法。但是,传统装饰器和现代装饰器之间的继承有所不同。
- 在传统装饰器中,如果超类有一个用 `@action` 装饰的字段,子类试图覆盖同一个字段,它将抛出 `TypeError: Cannot redefine property` 错误。
- 在现代装饰器中,如果超类有一个用 `@action` 装饰的字段,子类试图覆盖同一个字段,则允许覆盖该字段。但是,子类上的字段不是动作,除非它也在子类声明中用 `@action` 装饰。
使用 `observer` 作为装饰器
来自 `mobx-react` 的 `observer` 函数既是函数,也是装饰器,可用于类组件。
@observer
class Timer extends React.Component {
/* ... */
}