MobX 中文网

MobX 中文网

  • API 文档
  • Nodejs.cn 旗下网站

›MobX 核心

介绍

  • 关于 MobX
  • 关于本文档
  • 安装
  • MobX 的要点

MobX 核心

  • 可观察状态
  • 操作
  • 计算
  • 反应 {🚀}
  • API

MobX 与 React

  • React 集成
  • React 优化 {🚀}

提示与技巧

  • 定义数据存储
  • 了解反应性
  • 子类化
  • 分析反应性 {🚀}
  • 使用参数进行计算 {🚀}
  • MobX-utils {🚀}
  • 自定义可观察值 {🚀}
  • 惰性可观察量 {🚀}
  • 集合实用程序 {🚀}
  • 拦截和观察 {🚀}

微调

  • 配置 {🚀}
  • 装饰器 {🚀}
  • 从 MobX 4/5 迁移 {🚀}

创建可观察的状态

¥Creating observable state

属性、整个对象、数组、映射和集合都可以被观察。使对象可观察的基础是使用 makeObservable 为每个属性指定一个注释。最重要的注释是:

¥Properties, entire objects, arrays, Maps and Sets can all be made observable. The basics of making objects observable is specifying an annotation per property using makeObservable. The most important annotations are:

  • observable 定义了一个存储状态的可跟踪字段。

    ¥observable defines a trackable field that stores the state.

  • action 将方法标记为将修改状态的操作。

    ¥action marks a method as an action that will modify the state.

  • computed 标记一个 getter,它将从状态中导出新事实并缓存其输出。

    ¥computed marks a getter that will derive new facts from the state and cache its output.

makeObservable

用法:

¥Usage:

  • makeObservable(target, annotations?, options?)

该函数可用于使现有对象属性可观察。任何 JavaScript 对象(包括类实例)都可以传递到 target。通常 makeObservable 用于类的构造函数,其第一个参数是 this。annotations 参数将 annotations 映射到每个成员。只有带注释的成员才会受到影响。

¥This function can be used to make existing object properties observable. Any JavaScript object (including class instances) can be passed into target. Typically makeObservable is used in the constructor of a class, and its first argument is this. The annotations argument maps annotations to each member. Only annotated members are affected.

或者,可以在类成员上使用像 @observable 这样的装饰器,而不是在构造函数中调用 makeObservable。

¥Alternatively, decorators like @observable can be used on class members instead of calling makeObservable in the constructor.

导出信息并接受参数的方法(例如 findUsersOlderThan(age: number): User[])不能注释为 computed – 当从反应中调用它们时,它们的读取操作仍将被跟踪,但它们的输出不会被记忆以避免内存泄漏。要记住这些方法,你可以使用 MobX-utils 计算 Fn {🚀} 代替。

¥Methods that derive information and take arguments (for example findUsersOlderThan(age: number): User[]) can not be annotated as computed – their read operations will still be tracked when they are called from a reaction, but their output won't be memoized to avoid memory leaks. To memoize such methods you can use MobX-utils computedFn {🚀} instead.

支持子类化,但有一些限制 通过使用 override 注释(参见示例 此处)。

¥Subclassing is supported with some limitations by using the override annotation (see the example here).

class + makeObservable
class + decorators
factory function + makeAutoObservable
observable
class + decorators (legacy)
import { makeObservable, observable, computed, action, flow } from "mobx"

class Doubler {
value

constructor(value) {
makeObservable(this, {
value: observable,
double: computed,
increment: action,
fetch: flow
})
this.value = value
}

get double() {
return this.value * 2
}

increment() {
this.value++
}

*fetch() {
const response = yield fetch("/api/value")
this.value = response.json()
}
}

所有带注释的字段都是不可配置的。
所有不可观察(无状态)字段(action、flow)都是不可写的。

¥All annotated fields are non-configurable.
All non-observable (stateless) fields (action, flow) are non-writable.

使用现代装饰器时,无需调用 makeObservable,下面是基于装饰器的类的样子。请注意,@observable 注释应始终与 accessor 关键字结合使用。

¥When using modern decorators, there is no need to call makeObservable, below is what a decorator based class looks like. Note that the @observable annotation should always be used in combination with the accessor keyword.

import { observable, computed, action, flow } from "mobx"

class Doubler {
@observable accessor value

constructor(value) {
this.value = value
}

@computed
get double() {
return this.value * 2
}

@action
increment() {
this.value++
}

@flow
*fetch() {
const response = yield fetch("/api/value")
this.value = response.json()
}
}
import { makeAutoObservable } from "mobx"

function createDoubler(value) {
return makeAutoObservable({
value,
get double() {
return this.value * 2
},
increment() {
this.value++
}
})
}

请注意,类也可以利用 makeAutoObservable。示例中的差异只是展示了 MobX 如何应用于不同的编程风格。

¥Note that classes can leverage makeAutoObservable as well. The difference in the examples just demonstrate how MobX can be applied to different programming styles.

import { observable } from "mobx"

const todosById = observable({
"TODO-123": {
title: "find a decent task management system",
done: false
}
})

todosById["TODO-456"] = {
title: "close all tickets older than two weeks",
done: true
}

const tags = observable(["high prio", "medium prio", "low prio"])
tags.push("prio: for fun")

与 makeObservable 的第一个示例相比,observable 支持向对象添加(和删除)字段。这使得 observable 非常适合动态键控对象、数组、映射和集合等集合。

¥In contrast to the first example with makeObservable, observable supports adding (and removing) fields to an object. This makes observable great for collections like dynamically keyed objects, arrays, Maps and Sets.

要使用旧版装饰器,应在构造函数中调用 makeObservable(this) 以确保装饰器正常工作。

¥To use legacy decorators, makeObservable(this) should be called in the constructor to make sure decorators work.

import { observable, computed, action, flow } from "mobx"

class Doubler {
@observable value

constructor(value) {
makeObservable(this)
this.value = value
}

@computed
get double() {
return this.value * 2
}

@action
increment() {
this.value++
}

@flow
*fetch() {
const response = yield fetch("/api/value")
this.value = response.json()
}
}

makeAutoObservable

用法:

¥Usage:

  • makeAutoObservable(target, overrides?, options?)

makeAutoObservable 就像加强版的 makeObservable,因为它默认推断出所有属性。但是,你可以使用 overrides 参数来覆盖具有特定注释的默认行为 - 特别是 false 可用于完全排除属性或方法的处理。查看上面的代码作为示例。

¥makeAutoObservable is like makeObservable on steroids, as it infers all the properties by default. You can however use the overrides parameter to override the default behavior with specific annotations — in particular false can be used to exclude a property or method from being processed entirely. Check out the code above for an example.

makeAutoObservable 函数比使用 makeObservable 更紧凑且更易于维护,因为不必明确提及新成员。但是,makeAutoObservable 不能用于具有 super 或 subclassed 的类。

¥The makeAutoObservable function can be more compact and easier to maintain than using makeObservable, since new members don't have to be mentioned explicitly. However, makeAutoObservable cannot be used on classes that have super or are subclassed.

推断规则:

¥Inference rules:

  • 所有拥有的属性都变成 observable。

    ¥All own properties become observable.

  • 所有 getters 都变成 computed。

    ¥All getters become computed.

  • 所有 setters 都变成 action。

    ¥All setters become action.

  • 所有功能都变成 autoAction。

    ¥All functions become autoAction.

  • 所有生成器功能都变为 flow。(请注意,在某些转译器配置中无法检测到生成器函数,如果流程未按预期工作,请确保显式指定 flow。)

    ¥All generator functions become flow. (Note that generator functions are not detectable in some transpiler configurations, if flow doesn't work as expected, make sure to specify flow explicitly.)

  • overrides 参数中标有 false 的成员将不会被注释。例如,将其用于只读字段,例如标识符。

    ¥Members marked with false in the overrides argument will not be annotated. For example, using it for read only fields such as identifiers.

observable

用法:

¥Usage:

  • observable(source, overrides?, options?)

  • @observable accessor(字段装饰器)

    ¥@observable accessor (field decorator)

observable 注释也可以作为函数调用,以使整个对象立即可观察。source 对象将被克隆,所有成员都将变得可观察,类似于 makeAutoObservable 的做法。同样,可以提供 overrides 图来指定特定成员的注释。查看上面的代码块作为示例。

¥The observable annotation can also be called as a function to make an entire object observable at once. The source object will be cloned and all members will be made observable, similar to how it would be done by makeAutoObservable. Likewise, an overrides map can be provided to specify the annotations of specific members. Check out the above code block for an example.

observable 返回的对象将是一个代理,这意味着稍后添加到该对象的属性也将被拾取并变得可观察(除非 代理使用 被禁用)。

¥The object returned by observable will be a Proxy, which means that properties that are added later to the object will be picked up and made observable as well (except when proxy usage is disabled).

observable 方法也可以使用 arrays、映射 和 集合 等集合类型来调用。它们也将被克隆并转换成可观察的对应物。

¥The observable method can also be called with collections types like arrays, Maps and Sets. Those will be cloned as well and converted into their observable counterparts.

Example: observable array

以下示例创建一个可观察对象并使用 autorun 观察它。使用 Map 和 Set 集合的工作方式类似。

¥The following example creates an observable and observes it using autorun. Working with Map and Set collections works similarly.

import { observable, autorun } from "mobx"

const todos = observable([
    { title: "Spoil tea", completed: true },
    { title: "Make coffee", completed: false }
])

autorun(() => {
    console.log(
        "Remaining:",
        todos
            .filter(todo => !todo.completed)
            .map(todo => todo.title)
            .join(", ")
    )
})
// Prints: 'Remaining: Make coffee'

todos[0].completed = false
// Prints: 'Remaining: Spoil tea, Make coffee'

todos[2] = { title: "Take a nap", completed: false }
// Prints: 'Remaining: Spoil tea, Make coffee, Take a nap'

todos.shift()
// Prints: 'Remaining: Make coffee, Take a nap'

可观察数组还有一些额外的实用函数:

¥Observable arrays have some additional nifty utility functions:

  • clear() 从数组中删除所有当前条目。

    ¥clear() removes all current entries from the array.

  • replace(newItems) 将数组中的所有现有条目替换为新条目。

    ¥replace(newItems) replaces all existing entries in the array with new ones.

  • remove(value) 按值从数组中删除单个项目。如果找到并删除该项目,则返回 true。

    ¥remove(value) removes a single item by value from the array. Returns true if the item was found and removed.

Note: primitives and class instances are never converted to observables

原始值不能被 MobX 观察到,因为它们在 JavaScript 中是不可变的(但它们可以是 boxed)。尽管这种机制通常在库之外没有用处。

¥Primitive values cannot be made observable by MobX since they are immutable in JavaScript (but they can be boxed). Although there is typically no use for this mechanism outside libraries.

类实例永远不会通过将它们传递给 observable 或将它们分配给 observable 属性而自动变得可观察。使类成员可观察被认为是类构造函数的责任。

¥Class instances will never be made observable automatically by passing them to observable or assigning them to an observable property. Making class members observable is considered the responsibility of the class constructor.

{🚀} Tip: observable (proxied) versus makeObservable (unproxied)

make(Auto)Observable 和 observable 之间的主要区别在于,第一个修改作为第一个参数传入的对象,而 observable 创建一个可观察的克隆。

¥The primary difference between make(Auto)Observable and observable is that the first one modifies the object you are passing in as first argument, while observable creates a clone that is made observable.

第二个区别是 observable 创建了 Proxy 对象,以便在你将该对象用作动态查找映射时能够捕获将来添加的属性。如果你想要使其可观察的对象具有常规结构,其中所有成员都是预先已知的,我们建议使用 makeObservable,因为非代理对象速度更快一些,并且更容易在调试器中检查,而 console.log。

¥The second difference is that observable creates a Proxy object, to be able to trap future property additions in case you use the object as a dynamic lookup map. If the object you want to make observable has a regular structure where all members are known up-front, we recommend to use makeObservable as non proxied objects are a little faster, and they are easier to inspect in the debugger and console.log.

因此,make(Auto)Observable 是推荐在工厂函数中使用的 API。请注意,可以将 { proxy: false } 作为选项传递给 observable 以获取非代理克隆。

¥Because of that, make(Auto)Observable is the recommended API to use in factory functions. Note that it is possible to pass { proxy: false } as an option to observable to get a non proxied clone.

可用注释

¥Available annotations

注解描述
observable
observable.deep
定义存储状态的可跟踪字段。如果可能,分配给 observable 的任何值都会根据其类型自动转换为(深)observable、autoAction 或 flow。仅 plain object、array、Map、Set、function、generator function 可以转换。类实例和其他实例保持不变。
observable.ref与 observable 类似,但仅跟踪重新分配。分配的值将被完全忽略,并且不会自动转换为 observable/autoAction/flow。例如,如果你打算将不可变数据存储在可观察字段中,请使用此选项。
observable.shallow与 observable.ref 类似,但用于集合。分配的任何集合都将变得可观察,但集合本身的内容不会变得可观察。
observable.struct与 observable 类似,只不过结构上等于当前值的任何指定值都将被忽略。
action将方法标记为将修改状态的操作。查看 actions 了解更多详细信息。不可写。
action.bound与操作类似,但也会将操作绑定到实例,以便始终设置 this。不可写。
computed可用于 getter 以将其声明为可缓存的派生值。查看 computeds 了解更多详细信息。
computed.struct与 computed 类似,只不过如果重新计算后的结果在结构上与之前的结果相同,则不会通知任何观察者。
true推断最佳注释。查看 makeAutoObservable 了解更多详细信息。
false明确不要注释此属性。
flow创建 flow 来管理异步进程。查看 flow 了解更多详细信息。请注意,TypeScript 中推断的返回类型可能会关闭。不可写。
flow.bound与流类似,但也会将流绑定到实例,以便始终设置 this。不可写。
override适用于继承的 action、flow、computed、action.bound 被子类覆盖。
autoAction不应显式使用,但由 makeAutoObservable 在幕后使用,根据其调用上下文来标记可充当操作或派生的方法。将在运行时确定该函数是派生函数还是动作函数。

局限性

¥Limitations

  1. make(Auto)Observable 仅支持已定义的属性。确保你的 编译器配置正确(或者作为解决方法)在使用 make(Auto)Observable 之前已将值分配给所有属性。如果没有正确的配置,声明但未初始化的字段(如 class X { y; } 中)将无法正确选取。

    ¥make(Auto)Observable only supports properties that are already defined. Make sure your compiler configuration is correct, or as work-around, that a value is assigned to all properties before using make(Auto)Observable. Without correct configuration, fields that are declared but not initialized (like in class X { y; }) will not be picked up correctly.

  2. makeObservable 只能注释其自己的类定义声明的属性。如果子类或超类引入了可观察字段,则它本身必须为这些属性调用 makeObservable。

    ¥makeObservable can only annotate properties declared by its own class definition. If a sub- or superclass introduces observable fields, it will have to call makeObservable for those properties itself.

  3. options 参数只能提供一次。通过的 options 为 "sticky",以后不能更改(例如,在 subclass 中)。

    ¥options argument can be provided only once. Passed options are "sticky" and can NOT be changed later (eg. in subclass).

  4. 每个字段只能注释一次(override 除外)。subclass 中的字段注释或配置不能更改。

    ¥Every field can be annotated only once (except for override). The field annotation or configuration can't change in subclass.

  5. 非普通对象(类)的所有带注释的字段都是不可配置的。
    可以通过 configure({ safeDescriptors: false }) {🚀☣️} 禁用.

    ¥All annotated fields of non-plain objects (classes) are non-configurable.
    Can be disabled with configure({ safeDescriptors: false }) {🚀☣️} .

  6. 所有不可观察(无状态)字段(action、flow)都是不可写的。
    可以通过 configure({ safeDescriptors: false }) {🚀☣️} 禁用。

    ¥All non-observable (stateless) fields (action, flow) are non-writable.
    Can be disabled with configure({ safeDescriptors: false }) {🚀☣️} .

  7. 只有原型上定义的 action、computed、flow、action.bound 可以被子类覆盖。

    ¥Only action, computed, flow, action.bound defined on prototype can be overridden by subclass.

  8. 默认情况下,TypeScript 不允许你注释私有字段。这可以通过显式传递相关私有字段作为通用参数来克服,如下所示:makeObservable<MyStore, "privateField" | "privateField2">(this, { privateField: observable, privateField2: observable })

    ¥By default TypeScript will not allow you to annotate private fields. This can be overcome by explicitly passing the relevant private fields as generic argument, like this: makeObservable<MyStore, "privateField" | "privateField2">(this, { privateField: observable, privateField2: observable })

  9. 调用 make(Auto)Observable 并提供注释必须无条件完成,因为这样可以缓存推断结果。

    ¥Calling make(Auto)Observable and providing annotations must be done unconditionally, as this makes it possible to cache the inference results.

  10. 不支持在调用 make(Auto)Observable 后修改原型。

    ¥Modifying prototypes after make(Auto)Observable has been called is not supported.

  11. make(Auto)Observable 不支持 EcmaScript 私有字段(#field)。改用自动访问器 + Stage-3 装饰器 (@observable accessor #field) 语法。否则,在使用 TypeScript 时,建议使用 private 修饰符。

    ¥EcmaScript private fields (#field) are not supported by make(Auto)Observable. Use auto-accessor + Stage-3 decorators (@observable accessor #field) syntax instead. Otherwise, when using TypeScript, it is recommended to use the private modifier.

  12. 不支持在单个继承链中混合注释和装饰器 - 例如。 你不能对超类使用装饰器,对子类使用注释。

    ¥Mixing annotations and decorators within single inheritance chain is not supported - eg. you can't use decorators for superclass and annotations for subclass.

  13. makeObservable,extendObservable 不能用于其他内置可观察类型(ObservableMap、ObservableSet、ObservableArray 等)

    ¥makeObservable,extendObservable cannot be used on other builtin observable types (ObservableMap, ObservableSet, ObservableArray, etc)

  14. makeObservable(Object.create(prototype)) 将属性从 prototype 复制到创建的对象并使它们成为 observable。这种行为是错误的、意外的,因此已被弃用,并且可能会在未来的版本中发生变化。不要依赖它。

    ¥makeObservable(Object.create(prototype)) copies properties from prototype to created object and makes them observable. This behavior is wrong, unexpected and therefore deprecated and will likely change in future versions. Don't rely on it.

选项 {🚀}

¥Options {🚀}

上述 API 采用可选的 options 参数,该参数是支持以下选项的对象:

¥The above APIs take an optional options argument which is an object that supports the following options:

  • autoBind: true 默认使用 action.bound/flow.bound,而不是 action/flow。不影响显式注释的成员。

    ¥autoBind: true uses action.bound/flow.bound by default, rather than action/flow. Does not affect explicitely annotated members.

  • deep: false 默认使用 observable.ref,而不是 observable。不影响显式注释的成员。

    ¥deep: false uses observable.ref by default, rather than observable. Does not affect explicitely annotated members.

  • name: <string> 为对象提供一个调试名称,该名称打印在错误消息和反射 API 中。

    ¥name: <string> gives the object a debug name that is printed in error messages and reflection APIs.

  • proxy: false 强制 observable(thing) 使用非 proxy 实现。如果对象的形状不会随时间变化,这是一个不错的选择,因为非代理对象更容易调试且速度更快。此选项不适用于 make(Auto)Observable,请参阅 避免代理。

    ¥proxy: false forces observable(thing) to use non-proxy implementation. This is a good option if the shape of the object will not change over time, as non-proxied objects are easier to debug and faster. This option is not available for make(Auto)Observable, see avoiding proxies.

Note: options are sticky and can be provided only once options argument can be provided only for target that is NOT observable yet.
It is NOT possible to change options once the observable object was initialized.
Options are stored on target and respected by subsequent makeObservable/extendObservable calls.
You can't pass different options in subclass.

将可观察量转换回普通 JavaScript 集合

¥Converting observables back to vanilla JavaScript collections

有时有必要将可观察的数据结构转换回其普通的对应结构。例如,当将可观察对象传递给无法跟踪可观察对象的 React 组件时,或者获取不应进一步修改的克隆时。

¥Sometimes it is necessary to convert observable data structures back to their vanilla counterparts. For example when passing observable objects to a React component that can't track observables, or to obtain a clone that should not be further mutated.

要浅层转换集合,通常的 JavaScript 机制可以工作:

¥To convert a collection shallowly, the usual JavaScript mechanisms work:

const plainObject = { ...observableObject }
const plainArray = observableArray.slice()
const plainMap = new Map(observableMap)

要将数据树递归转换为普通对象,可以使用 toJS 实用程序。对于类,建议实现 toJSON() 方法,因为它将被 JSON.stringify 选取。

¥To convert a data tree recursively to plain objects, the toJS utility can be used. For classes, it is recommended to implement a toJSON() method, as it will be picked up by JSON.stringify.

关于类的简短说明

¥A short note on classes

到目前为止,上面的大多数示例都倾向于类语法。MobX 原则上对此不持任何意见,并且可能有同样多的 MobX 用户使用普通对象。然而,类的一个小好处是它们具有更容易发现的 API,例如 TypeScript。此外,instanceof 检查对于类型推断来说非常强大,并且类实例没有封装在 Proxy 对象中,这让它们在调试器中获得更好的体验。最后,类受益于大量引擎优化,因为它们的形状是可预测的,并且方法在原型上共享。但是繁重的继承模式很容易成为步兵,所以如果你使用类,请保持它们简单。因此,尽管人们稍微倾向于使用类,但如果这种风格更适合你,我们绝对希望鼓励你偏离这种风格。

¥So far most examples above have been leaning towards the class syntax. MobX is in principle unopinionated about this, and there are probably just as many MobX users that use plain objects. However, a slight benefit of classes is that they have more easily discoverable APIs, e.g. TypeScript. Also, instanceof checks are really powerful for type inference, and class instances aren't wrapped in Proxy objects, giving them a better experience in debuggers. Finally, classes benefit from a lot of engine optimizations, since their shape is predictable, and methods are shared on the prototype. But heavy inheritance patterns can easily become foot-guns, so if you use classes, keep them simple. So, even though there is a slight preference to use classes, we definitely want to encourage you to deviate from this style if that suits you better.

← MobX 的要点操作 →
  • makeObservable
  • makeAutoObservable
  • observable
  • 可用注释
  • 局限性
  • 选项 {🚀}
  • 将可观察量转换回普通 JavaScript 集合
  • 关于类的简短说明
MobX v6.13 中文网 - 粤ICP备13048890号
Nodejs.cn 旗下网站