装饰器
¥Decorators
启用装饰器
¥Enabling decorators
经过多年的修改,ES 装饰器终于达到了 TC39 流程的第 3 阶段,这意味着它们相当稳定,不会像之前的装饰器提案那样再次经历重大变化。MobX 已经实现了对这种新的 "2022.3/第 3 阶段" 装饰器语法的支持。使用现代装饰器,不再需要调用 makeObservable
/ makeAutoObservable
。
¥After years of alterations, ES decorators have finally reached Stage 3 in the TC39 process, meaning that they are quite stable and won't undergo breaking changes again like the previous decorator proposals have. MobX has implemented support for this new "2022.3/Stage 3" decorator syntax.
With modern decorators, it is no longer needed to call makeObservable
/ makeAutoObservable
.
2022.3 装饰器受以下支持:
¥2022.3 Decorators are supported in:
TypeScript(5.0 及更高版本,确保未启用
experimentalDecorators
标志)。示例提交。¥TypeScript (5.0 and higher, make sure that the
experimentalDecorators
flag is NOT enabled). Example commit.对于 Babel,请确保插件
proposal-decorators
已启用最高版本(当前为2023-05
)。示例提交。¥For Babel make sure the plugin
proposal-decorators
is enabled with the highest version (currently2023-05
). Example commit.
// tsconfig.json
{
"compilerOptions": {
"experimentalDecorators": false /* or just remove the flag */
}
}
// babel.config.json (or equivalent)
{
"plugins": [
[
"@babel/plugin-proposal-decorators",
{
"version": "2023-05"
}
]
]
}
使用装饰器
¥Using decorators
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 规范的一部分,如果你想使用现代装饰器,则需要它。
¥Notice the usage of the new accessor
keyword when using @observable
.
It is part of the 2022.3 spec and is required if you want to use modern decorators.
Using legacy decorators
我们不建议代码库使用 TypeScript / Babel 旧版装饰器,因为它们永远不会成为该语言的正式部分,但你仍然可以使用它们。它确实需要特定的转译设置:
¥We do not recommend codebases to use TypeScript / Babel legacy decorators since they well never become an official part of the language, but you can still use them. It does require a specific setup for transpilation:
版本 6 之前的 MobX 鼓励使用旧版装饰器并将事物标记为 observable
、computed
和 action
。虽然 MobX 6 建议不要使用这些装饰器(而是使用现代装饰器或 makeObservable
/makeAutoObservable
),但在当前的主要版本中仍然是可能的。MobX 7 中将删除对旧版装饰器的支持。
¥MobX before version 6 encouraged the use of legacy decorators and mark things as observable
, computed
and action
.
While MobX 6 recommends against using these decorators (and instead use either modern decorators or makeObservable
/ makeAutoObservable
), it is in the current major version still possible.
Support for legacy decorators will be removed in 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)
}
}
Migrating from legacy decorators
要从传统装饰器迁移到现代装饰器,请执行以下步骤:
¥To migrate from legacy decorators to modern decorators, perform the following steps:
从 TypeScript 配置(或 Babel 等效配置)中禁用/删除
experimentalDecorators
标志¥Disable / remove the
experimentalDecorators
flag from your TypeScript configuration (or Babel equivalent)从使用装饰器的类构造函数中删除所有
makeObservable(this)
调用。¥Remove all
makeObservable(this)
calls from class constructors that use decorators.将
@observable
(及其变体)的所有实例替换为@observable accessor
¥Replace all instances of
@observable
(and variations) with@observable accessor
Decorator changes / gotchas
MobX 2022.3 装饰器与 MobX 5 装饰器非常相似,因此用法基本相同,但存在一些问题:
¥MobX' 2022.3 Decorators are very similar to the MobX 5 decorators, so usage is mostly the same, but there are some gotchas:
@observable accessor
装饰器是不可枚举的。accessor
在过去没有直接对应词 - 它们是语言中的一个新概念。我们选择使它们成为不可枚举、非拥有的属性,以便更好地遵循 ES 语言的精神和accessor
的含义。可枚举性的主要案例似乎是围绕序列化和剩余解构。¥
@observable accessor
decorators are not enumerable.accessor
s do not have a direct equivalent in the past - they're a new concept in the language. We've chosen to make them non-enumerable, non-own properties in order to better follow the spirit of the ES language and whataccessor
means. The main cases for enumerability seem to have been around serialization and rest destructuring.关于序列化,隐式序列化所有属性在 OOP 世界中可能并不理想,因此这似乎不是一个重大问题(考虑实现
toJSON
或使用serializr
作为可能的替代方案)¥Regarding serialization, implicitly serializing all properties probably isn't ideal in an OOP-world anyway, so this doesn't seem like a substantial issue (consider implementing
toJSON
or usingserializr
as possible alternatives)解决剩余解构问题,这是 MobX 中的反模式 - 这样做会(可能是不必要的)触及所有可观察量并使观察者过度反应)。
¥Addressing rest-destructuring, such is an anti-pattern in MobX - doing so would (likely unwantedly) touch all observables and make the observer overly-reactive).
@action some_field = () => {}
过去和现在都是有效用法(如果还使用了makeObservable()
)。但是,@action accessor some_field = () => {}
永远无效。¥
@action some_field = () => {}
was and is valid usage (ifmakeObservable()
is also used). However,@action accessor some_field = () => {}
is never valid.
observer
作为装饰器
使用 ¥Using observer
as a decorator
mobx-react
中的 observer
函数既是一个函数,又是一个装饰器,可以在类组件上使用:
¥The observer
function from mobx-react
is both a function and a decorator that can be used on class components:
@observer
class Timer extends React.Component {
/* ... */
}