MobX 🇺🇦

MobX 🇺🇦

  • API 参考
  • 中文
  • 한국어
  • 赞助商
  • GitHub

›MobX 核心

介绍

  • 关于 MobX
  • 关于此文档
  • 安装
  • MobX 的核心

MobX 核心

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

MobX 和 React

  • React 集成
  • React 优化 {🚀}

技巧和窍门

  • 定义数据存储
  • 理解反应性
  • 子类化
  • 分析反应性 {🚀}
  • 带参数的计算值 {🚀}
  • MobX-utils {🚀}
  • 自定义可观察值 {🚀}
  • 延迟可观察值 {🚀}
  • 集合工具 {🚀}
  • 拦截和观察 {🚀}

微调

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

创建可观察状态

属性、整个对象、数组、Map 和 Set 都可以被设为可观察的。使对象可观察的基本方法是使用 makeObservable 为每个属性指定一个注解。最重要的注解是

  • observable 定义了一个可跟踪的字段,用于存储状态。
  • action 将一个方法标记为一个会修改状态的动作。
  • computed 将一个 getter 标记为一个从状态中推导出新事实并缓存其输出的 getter。

makeObservable

用法

  • makeObservable(target, annotations?, options?)

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

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

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

子类化在使用 override 注解的情况下得到支持,但也有一些限制(请参阅 此处 的示例)。

类 + makeObservable
类 + 装饰器
工厂函数 + makeAutoObservable
observable
类 + 装饰器(遗留)
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)都是 不可写的。

当使用现代装饰器时,无需调用 makeObservable,下面是一个基于装饰器的类的示例。请注意,@observable 注解应始终与 accessor 关键字一起使用。

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 如何应用于不同的编程风格。

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 非常适合像动态键对象、数组、Map 和 Set 这样的集合。

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

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

用法

  • makeAutoObservable(target, overrides?, options?)

makeAutoObservable 就像 makeObservable 的加强版,因为它默认情况下会推断所有属性。但是,您可以使用 overrides 参数来覆盖默认行为,使用特定的注解 - 特别是 false 可以用来完全排除一个属性或方法。查看上面的代码块以了解示例。

makeAutoObservable 函数可能比使用 makeObservable 更简洁,更容易维护,因为新成员无需显式提及。但是,makeAutoObservable 不能用在有超类或 子类 的类上。

推断规则

  • 所有自身属性都变成 observable。
  • 所有 getters 都变成 computed。
  • 所有 setters 都变成 action。
  • 所有函数都变成 autoAction。
  • 所有生成器函数都变成 flow。(请注意,在某些编译器配置中,生成器函数是不可检测的,如果 flow 不能按预期工作,请确保显式指定 flow。)
  • 在 overrides 参数中用 false 标记的成员不会被注解。例如,使用它来标识只读字段,例如标识符。

observable

用法

  • observable(source, overrides?, options?)
  • @observable accessor (字段装饰器)

observable 注解也可以作为一个函数来调用,以一次性使整个对象可观察。source 对象将被克隆,所有成员都将被设为可观察的,类似于 makeAutoObservable 的操作方式。同样,也可以提供一个 overrides 地图来指定特定成员的注解。查看上面的代码块以了解示例。

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

observable 方法也可以与 数组、Map 和 Set 这样的集合类型一起调用。它们也会被克隆并转换为其可观察的对应物。

示例: 可观察的数组

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

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'

可观察的数组有一些额外的实用功能

  • clear() 从数组中删除所有当前条目。
  • replace(newItems) 用新的条目替换数组中所有现有的条目。
  • remove(value) 从数组中按值删除单个项目。如果项目被找到并删除,则返回 true。

注意: 基本类型和类实例永远不会被转换为可观察的

基本类型的值不能被 MobX 设为可观察的,因为它们在 JavaScript 中是不可变的(但它们可以被 装箱)。虽然在库之外通常没有使用这种机制的必要。

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

{🚀} 技巧: 可观察的(代理的)与 makeObservable(非代理的)

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

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

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

可用注解

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

限制

  1. make(Auto)Observable 仅支持已定义的属性。 确保您的 编译器配置 正确,或者作为变通方法,在使用 make(Auto)Observable 之前为所有属性分配一个值。 如果配置不正确,将不会正确获取声明但未初始化的字段(如 class X { y; } 中的字段)。
  2. makeObservable 只能注释其自身类定义声明的属性。 如果子类或超类引入了可观察字段,则必须为此类属性自己调用 makeObservable。
  3. options 参数只能提供一次。 传递的 options 是“粘性” 的,不能在以后(例如在 子类 中)更改。
  4. 每个字段只能注释一次(override 除外)。 字段注释或配置不能在 子类 中更改。
  5. 所有注释 的非纯对象(类)字段都是不可配置的。
    可以通过 configure({ safeDescriptors: false }) {🚀☣️} 禁用。.
  6. 所有不可观察的(无状态)字段(action、flow)都是 不可写的。
    可以通过 configure({ safeDescriptors: false }) {🚀☣️} 禁用。.
  7. 只有在原型 上定义的action、computed、flow、action.bound 可以被子类覆盖.
  8. 默认情况下,TypeScript 不会允许您注释私有字段。 这可以通过显式地将相关的私有字段作为泛型参数传递来克服,例如:makeObservable<MyStore, "privateField" | "privateField2">(this, { privateField: observable, privateField2: observable })
  9. 调用 make(Auto)Observable 并提供注释必须无条件进行,因为这使得缓存推断结果成为可能。
  10. 在调用 make(Auto)Observable 后修改原型是不支持的。
  11. EcmaScript 私有字段(#field)是不支持的。 使用 TypeScript 时,建议使用 private 修饰符。
  12. 混合注释和装饰器 在单个继承链中是不支持的 - 例如,您不能对超类使用装饰器,对子类使用注释。
  13. makeObservable、extendObservable 不能用于其他内置可观察类型(ObservableMap、ObservableSet、ObservableArray 等)
  14. makeObservable(Object.create(prototype)) 将属性从 prototype 复制到创建的对象,并使其 observable。 此行为是错误的,出乎意料的,因此已弃用,并且很可能在将来的版本中发生变化。 不要依赖它。

选项 {🚀}

上述 API 接受一个可选的 options 参数,它是一个支持以下选项的对象

  • autoBind: true 默认情况下使用 action.bound/flow.bound,而不是 action/flow。 不影响显式注释的成员。
  • deep: false 默认情况下使用 observable.ref,而不是 observable。 不影响显式注释的成员。
  • name: <string> 给对象一个调试名称,该名称在错误消息和反射 API 中打印。
  • proxy: false 强制 observable(thing) 使用非-代理 实现。 如果对象的形状不会随着时间的推移而改变,这是一个不错的选择,因为非代理对象更容易调试并且速度更快。 此选项不可用于 make(Auto)Observable,请参阅 避免代理。

注意: 选项是粘性 的,只能提供一次 options 参数只能为尚未可观察的 target 提供。
一旦可观察对象被初始化,就无法更改选项。
选项存储在目标上,并受后续 makeObservable/extendObservable 调用的尊重。
您不能在 子类 中传递不同的选项。

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

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

要浅层地转换集合,可以使用通常的 JavaScript 机制

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

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

关于类的简短说明

到目前为止,上面大多数示例都倾向于类语法。 原则上,MobX 对此没有意见,可能也有同样多的 MobX 用户使用普通对象。 但是,类的轻微优势在于它们具有更容易发现的 API,例如 TypeScript。 此外,instanceof 检查对于类型推断非常有用,类实例不会被包装在 Proxy 对象中,这使它们在调试器中具有更好的体验。 最后,类受益于许多引擎优化,因为它们的形状是可预测的,方法在原型上共享。 但是,繁重的继承模式很容易成为脚枪,因此如果您使用类,请保持简单。 因此,即使存在轻微的偏好使用类,我们也绝对希望鼓励您在更适合您的情况下偏离这种风格。

← MobX 的要点操作 →
  • makeObservable
  • makeAutoObservable
  • observable
  • 可用注释
  • 限制
  • 选项 {🚀}
  • 将可观察对象转换回普通 JavaScript 集合
  • 关于类的简短说明
MobX 🇺🇦
文档
关于 MobXMobX 的要点
社区
GitHub 讨论 (NEW)Stack Overflow
更多
星标