创建可观察状态
属性,整个对象,数组,映射和集合都可以被观察到。使对象可观察的基础是使用 makeObservable
来为每个属性指定一个注释 。最重要的注释是:
-
observable
定义存储状态的可跟踪字段。 -
action
将方法标记为将修改状态的操作。 -
computed
标记一个将从状态中获取新事实并缓存其输出的获取器。
数组,地图和集合之类的集合将自动变为可观察的。
makeObservable
用法:
makeObservable(target, annotations?, options?)
它可用于捕获现有对象属性并使它们可观察。任何JavaScript对象(包括类实例)都可以传递到target
中。通常makeObservable
用于类的构造函数中,其第一个参数为this
。该annotations
参数将注释映射到每个成员。请注意,在使用decorators
时,可以省略annotations
参数。
派生信息并接受参数的方法(例如findUsersOlderThan(age: number): User[]
)不需要任何注释。从响应调用它们时,仍会跟踪其读取操作,但是不会记住它们的输出,以避免内存泄漏。
class + makeObservable
import { makeObservable, observable, computed, action } from "mobx"
class Doubler {
value
constructor(value) {
makeObservable(this, {
value: observable,
double: computed,
increment: action
})
this.value = value
}
get double() {
return this.value * 2
}
increment() {
this.value++
}
}
function + makeObservable
import { makeAutoObservable } from "mobx"
function createDoubler(value) {
return makeAutoObservable({
value,
get double() {
return this.value * 2
},
increment() {
this.value++
}
})
}
请注意,类也可以利用makeAutoObservable。这些示例之间的差异仅说明了如何将MobX应用于不同的编程样式。
observable
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非常适合诸如动态键对象,数组,地图和集合之类的集合。
makeAutoObservable
用法:
makeAutoObservable(target, overrides?, options?)
makeAutoObservable
于makeObservable
类似,因为它会默认推断所有属性。您仍然可以使用overrides
来覆盖具有特定注释的默认行为。特别是false
可用于将属性或方法完全排除在处理之外。查看上面的代码标签以获取示例。makeAutoObservable
函数比使用makeObservable
更紧凑且更易于维护,因为不必明确提及新成员。但是,makeAutoObservable
不能用于具有super或subclassed的类。
推理规则:
包含
function
值的任何(继承的)成员都将用注释autoAction
。任何
get
三元都将带有computed
注释。其他任何自己的字段都将标记为
observable
。任何(继承的)作为生成器函数的成员都将带有注释
flow
。(请注意,在某些编译器配置中无法检测到生成器功能,如果流无法按预期运行,请确保flow
明确指定。)-
overrides
参数中被标为false
的成员将不会被注释。例如,将其用于只读字段(例如标识符)。observable
用法:
observable(source, overrides?, options?)
该observable
注释也可以作为一个函数调用,使整个对象马上被观察到。该source对象将被克隆,并且所有成员将变为可观察的,类似于makeAutoObservable的方式。同样,overrides可以提供地图以指定特定成员的注释。查看上面的代码块作为示例。
返回的对象observable将是Proxy,这意味着以后添加到该对象的属性也将被拾取并变为可观察(除非禁用了代理用法)。
observable也可以使用集合类型(如数组,地图和集合)调用该方法。这些也将被克隆并转换为可观察的对应对象。
可用的注释
注解 | 描述 |
---|---|
observable observable.deep | 定义一个存储状态的可跟踪字段。如果可能的话,任何分配给observable字段的值也将被递归地观察。也就是说,当且仅当值是纯对象,数组,Map或Set时。 |
observable.ref | 类似于observable,但仅会跟踪重新分配。分配的值本身不会自动变为可观察的。例如,如果要将不可变数据存储在可观察字段中,请使用此选项。 |
observable.shallow | 喜欢,observable.ref但用于收藏。分配的任何集合都将变为可观察的,但是集合本身的内容将变得不可观察。 |
observable.struct | 与相似observable,除了任何在结构上等于当前值的赋值都会被忽略。 |
action | 将方法标记为将修改状态的操作。查看操作以获取更多详细信息。 |
action.bound | 与动作类似,但也会将动作绑定到实例,以便this始终进行设置。 |
computed | 可以在getter上使用,以将其声明为可以缓存的派生值。查看计算出的更多详细信息。 |
computed.struct | 与相似computed,除了如果重新计算后的结果在结构上与先前的结果相同,则不会通知任何观察者。 |
true | 推断最佳注释。查看makeAutoObservable以获得更多详细信息。 |
false | 显式不注释此属性。 |
flow | 创建一个flow以管理异步过程。查看流程以获取更多详细信息。请注意,TypeScript中的推断返回类型可能已关闭。 |
autoAction | 不应显式使用,而应makeAutoObservable根据其调用上下文在后台使用,以标记可以充当操作或派生方法的方法。 |
局限性
-
make(Auto)Observable
仅支持已定义的属性。使用之前,请确保您的编译器配置正确或作为变通方法,即已将值分配给所有属性make(Auto)Observable
。没有正确的配置,class X { y; }
将无法正确拾取已声明但尚未初始化的字段(如中的)。 -
makeObservable
只能注释由其自己的类定义声明的属性。如果子类或超类引入了可观察字段,则它必须自己调用makeObservable
这些属性。 - 默认情况下,TypeScript不允许您注释私有字段。可以通过将相关的专用字段显式传递为通用参数来克服,例如:
makeObservable<MyStore, "myPrivateField" | "myPrivateField2">(this, { myPrivateField: observable, myPrivateField2: observable })
。 - 调用
make(Auto)Observable
和提供注释必须无条件完成,因为这样可以缓存推理结果。 - 不支持JavaScript私有字段(
#field
语法)。使用TypeScript时,建议改用private
修饰符。
选项
上面的API带有一个可选options
参数,该参数是一个支持以下选项的对象:
-
autoBind: true
自动将所有创建的操作绑定到实例。 -
deep: falseobservable.ref
默认情况下使用,而不是observable
创建新的可观察成员。 -
name: <string>
为对象提供一个调试名称,该名称将打印在错误消息和反射API中。
将可观察变量转换回原始JavaScript集合
有时有必要将可观察的数据结构转换回原始的数据结构。例如,当将可观察对象传递到无法跟踪可观察对象的React组件时,或获得不应进一步突变的克隆时。
要浅转换集合,通常的JavaScript机制起作用:
const plainObject = { ...observableObject }
const plainArray = observableArray.slice()
const plainMap = new Map(observableMap)
简短说明
到目前为止,以上大多数示例都倾向于使用类语法。MobX原则上对此没有限制,并且可能有使用纯对象的MobX用户数量一样多。但是,类的一个小好处是它们具有更容易发现的API,例如TypeScript。此外,instanceof
检查对于类型推断确实非常强大,并且类实例也不会包含在内Proxy
对象,使他们在调试器中获得更好的体验。最后,类可以从许多引擎优化中受益,因为它们的形状是可以预测的,并且方法在原型上是共享的。但是繁重的继承模式很容易成为foot脚,因此,如果您使用类,请使其保持简单。因此,即使稍微偏爱使用类,我们也绝对希望鼓励您脱离这种风格,如果它更适合您。