如何(不)使用装饰器

在MobX 中使用 ES.next 装饰器是可选的。本章节将解释如何(避免)使用它们。

使用装饰器语法的优势:

  • 样板文件最小化,声明式代码。
  • 易于使用和阅读。大多数 MobX 用户都在使用。

使用装饰器语法的劣势:

  • ES.next 2阶段特性。
  • 需要设置和编译,目前只有 Babel/Typescript 编译器支持。

在 MobX 中使用装饰器有两种方式。

  1. 开启编译器的实验性装饰器语法 (详细请参见下面)
  2. 不启用装饰器语法,而是利用 MobX 内置的工具 decorate 来对类和对象进行装饰。

使用装饰器语法:

  1. import { observable, computed, action } from "mobx";
  2. class Timer {
  3. @observable start = Date.now();
  4. @observable current = Date.now();
  5. @computed
  6. get elapsedTime() {
  7. return this.current - this.start + "milliseconds";
  8. }
  9. @action
  10. tick() {
  11. this.current = Date.now();
  12. }
  13. }

使用 decorate 工具:

  1. import { observable, computed, action, decorate } from "mobx";
  2. class Timer {
  3. start = Date.now();
  4. current = Date.now();
  5. get elapsedTime() {
  6. return this.current - this.start + "milliseconds";
  7. }
  8. tick() {
  9. this.current = Date.now()
  10. }
  11. }
  12. decorate(Timer, {
  13. start: observable,
  14. current: observable,
  15. elapsedTime: computed,
  16. tick: action
  17. })

想要在单个属性上应用多个装饰器的话,你可以传入一个装饰器数组。多个装饰器应用的顺序是从从右至左。

  1. import { decorate, observable } from "mobx"
  2. import { serializable, primitive } from "serializr"
  3. import persist from "mobx-persist";
  4. class Todo {
  5. id = Math.random();
  6. title = "";
  7. finished = false;
  8. }
  9. decorate(Todo, {
  10. title: [serializable(primitive), persist("object"), observable],
  11. finished: [serializable(primitive), observable]
  12. })

注意: 并非所有的装饰器都可以在一起组合,此功能只会尽力而为。一些装饰器会直接影响实例,并且可以“隐藏”其他那些只更改原型的装饰器的效果。


mobx-react 中的 observer 函数既是装饰器又是函数,这意味着下面这些语法都可以正常运行:

  1. @observer
  2. class Timer extends React.Component {
  3. /* ... */
  4. }
  5. const Timer = observer(class Timer extends React.Component {
  6. /* ... */
  7. })
  8. const Timer = observer((props) => (
  9. /* 渲染 */
  10. ))

启用装饰器语法

如果想使用装饰器,需要按照下列步骤。

TypeScript

tsconfig.json 中启用编译器选项 "experimentalDecorators": true

Babel: 使用 babel-preset-mobx

另外一种在 Babel 中配置 MobX 的方式是使用 mobx preset,这种方式更方便,其中包含了装饰器及其他几个经常与 mobx 一起使用的插件:

  1. npm install --save-dev babel-preset-mobx

.babelrc:

  1. {
  2. "presets": ["mobx"]
  3. }

Babel: 手动启用装饰器

要启用装饰器的支持而不使用 mobx preset 的话,需要按照下列步骤。安装支持装饰器所需依赖: npm i --save-dev babel-plugin-transform-decorators-legacy 。并在 .babelrc 文件中启用:

  1. {
  2. "presets": ["es2015", "stage-1"],
  3. "plugins": ["transform-decorators-legacy"]
  4. }

注意,插件的顺序很重要: transform-decorators-legacy 应该放在首位。babel 设置有问题?请先参考这个 issue

对于 babel 7, 参见 issue 1352 来查看设置示例。

装饰器语法 和 Create React App

  • create-react-app 目前还没有内置的装饰器支持。要解决这个问题,你可以使用 eject 命令 或使用 react-app-rewired

免责声明: 装饰器语法的局限性:

当前编译器所实现的装饰器语法是有一些限制的,而且与实际的装饰器语法表现并非完全一致。此外,在所有编译器都实现第二阶段的提议之前,许多组合模式目前都无法与装饰器一起使用。由于这个原因,目前在 MobX 中对装饰器语法支持的范围进行了限定,以确保支持的特性在所有环境中始终保持一致。

MobX 社区并没有正式支持以下模式:

  • 重新定义继承树中的装饰类成员
  • 装饰静态类成员
  • 将 MobX 提供的装饰器与其他装饰器组合
  • 热更新 (HMR) / React-hot-loader 可能不能正常运行

在第一次读/写到装饰属性之前,该属性在类实例上可能是不可见的。

(注意: 不支持并不意味着不能运行,它的意义在于如果不能正常运行的话,在官方规范的推进之前,提出的 issues 是不会被处理的。)