MobX 🇺🇦

MobX 🇺🇦

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

›MobX 核心

介绍

  • 关于 MobX
  • 关于本文档
  • 安装
  • MobX 的精髓

MobX 核心

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

MobX 和 React

  • React 集成
  • React 优化 {🚀}

提示和技巧

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

微调

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

使用计算值推导信息

用法

  • computed (注解)
  • computed(options) (注解)
  • computed(fn, options?)
  • @computed (getter 装饰器)
  • @computed(options) (getter 装饰器)

计算值可以用来从其他可观察对象中推导信息。它们延迟计算,缓存其输出,并且只有在底层可观察对象之一发生变化时才重新计算。如果它们不被任何东西观察,它们会完全暂停。

从概念上讲,它们与电子表格中的公式非常相似,不可低估。它们有助于减少必须存储的状态数量,并且高度优化。在可能的情况下使用它们。

例子

计算值可以通过使用 computed 注解 JavaScript getter 来创建。使用 makeObservable 将 getter 声明为计算值。如果你希望所有 getter 都自动声明为 computed,可以使用 makeAutoObservable、observable 或 extendObservable。计算 getter 成为不可枚举的。

为了说明计算值的意义,下面的例子依赖于 autorun,来自 反应 {🚀} 高级部分。

import { makeObservable, observable, computed, autorun } from "mobx"

class OrderLine {
    price = 0
    amount = 1

    constructor(price) {
        makeObservable(this, {
            price: observable,
            amount: observable,
            total: computed
        })
        this.price = price
    }

    get total() {
        console.log("Computing...")
        return this.price * this.amount
    }
}

const order = new OrderLine(0)

const stop = autorun(() => {
    console.log("Total: " + order.total)
})
// Computing...
// Total: 0

console.log(order.total)
// (No recomputing!)
// 0

order.amount = 5
// Computing...
// (No autorun)

order.price = 2
// Computing...
// Total: 10

stop()

order.price = 3
// Neither the computation nor autorun will be recomputed.

上面的例子很好地展示了 computed 值的优势,它充当缓存点。即使我们改变了 amount,并且这将触发 total 重新计算,它也不会触发 autorun,因为 total 会检测到它的输出没有受到影响,因此没有必要更新 autorun。

相比之下,如果 total 没有被注释,autorun 会运行其效果 3 次,因为它将直接依赖于 total 和 amount。自己试试看.

computed graph

这是上面例子中创建的依赖关系图。

规则

使用计算值时,有一些最佳实践需要遵循。

  1. 它们不应该有副作用或更新其他可观察对象。
  2. 避免创建和返回新的可观察对象。
  3. 它们不应该依赖于不可观察的值。

提示

提示:如果计算值没有被观察,它们将被暂停

有时,MobX 的新手会感到困惑,也许他们习惯于使用像 Reselect 这样的库,如果你创建了一个计算属性但没有在任何反应中使用它,它不会被记忆,并且看起来比必要时计算得更多。例如,如果我们在上面的例子中扩展了调用 console.log(order.total) 两次,在我们调用 stop() 之后,该值将被重新计算两次。

这使得 MobX 可以自动暂停那些没有积极使用的计算,以避免对那些没有被访问的计算值的无用更新。但如果一个计算属性没有被某个反应使用,那么计算表达式每次请求其值时都会被计算,因此它们的行为就像一个普通的属性。

如果你只对计算属性进行调整,它们可能看起来效率不高,但当应用在使用 observer、autorun 等等的项目中时,它们就会变得非常高效。

以下代码演示了这个问题。

// OrderLine has a computed property `total`.
const line = new OrderLine(2.0)

// If you access `line.total` outside of a reaction, it is recomputed every time.
setInterval(() => {
    console.log(line.total)
}, 60)

可以通过使用 keepAlive 选项设置注解来覆盖它 (自己试试看),或者创建一个无操作 autorun(() => { someObject.someComputed }),它可以在需要时被很好地清理掉。请注意,这两种方法都存在创建内存泄漏的风险。更改这里的默认行为是一种反模式。

MobX 也可以使用 computedRequiresReaction 选项进行配置,以便在计算值在非反应式上下文中访问时报告错误。

提示:计算值可以有 setter

也可以为计算值定义 setter。请注意,这些 setter 不能用于直接改变计算属性的值,但它们可以用作推导的“反向”。setter 自动标记为动作。例如。

class Dimension {
    length = 2

    constructor() {
        makeAutoObservable(this)
    }

    get squared() {
        return this.length * this.length
    }
    set squared(value) {
        this.length = Math.sqrt(value)
    }
}

{🚀} 提示:computed.struct 用于结构化比较输出

如果一个计算值的输出与之前的计算在结构上等效,不需要通知观察者,可以使用 computed.struct。它将在通知观察者之前首先进行结构化比较,而不是引用相等性检查。例如

class Box {
    width = 0
    height = 0

    constructor() {
        makeObservable(this, {
            width: observable,
            height: observable,
            topRight: computed.struct
        })
    }

    get topRight() {
        return {
            x: this.width,
            y: this.height
        }
    }
}

默认情况下,计算值的输出是通过引用进行比较的。由于上面的例子中的 topRight 将始终产生一个新的结果对象,它永远不会被认为与之前的输出相等。除非使用了 computed.struct。

然而,在上面的例子中,我们实际上不需要 computed.struct!计算值通常只在支持值发生变化时重新计算。这就是为什么 topRight 只有在 width 或 height 发生变化时才会做出反应。因为如果这两个值中的任何一个发生变化,我们无论如何都会得到一个不同的 topRight 坐标。computed.struct 永远不会命中缓存,而且是一种浪费,所以我们不需要它。

在实践中,computed.struct 的用处并不像听起来那样大。只有在底层可观察对象的更改仍然会导致相同输出时才使用它。例如,如果我们首先对坐标进行四舍五入,那么即使底层值不同,四舍五入后的坐标也可能与之前四舍五入后的坐标相等。

查看 equals 选项以获取关于确定输出是否已更改的进一步自定义。

{🚀} 提示:带参数的计算值

尽管 getter 不接受参数,但几种处理需要参数的派生值的方法在 这里 进行了讨论。

{🚀} 提示:使用 computed(expression) 创建独立的计算值

computed 也可以像 observable.box 创建一个独立的计算值那样直接作为函数调用。使用 .get() 对返回的对象进行操作,以获取计算的当前值。这种形式的 computed 并不经常使用,但在某些情况下,你需要传递一个“盒装”的计算值,它可能被证明是有用的,其中一个例子在 这里 进行了讨论。

选项 {🚀}

computed 通常按照你想要的方式工作,但可以通过传入一个 options 参数来定制它的行为。

name

这个字符串用作 Spy 事件监听器 和 MobX 开发者工具 中的调试名称。

equals

默认情况下设置为comparer.default。 它充当比较函数,用于比较前一个值和下一个值。 如果此函数认为这两个值相等,则观察者将不会重新评估。

这在处理结构化数据和来自其他库的类型时很有用。 例如,计算后的 moment 实例可以使用(a, b) => a.isSame(b)。 comparer.structural 和 comparer.shallow 非常方便,如果你想使用结构化/浅层比较来确定新值是否与前一个值不同,并因此通知其观察者。

查看上面的 computed.struct 部分。

内置比较器

MobX 提供了四种内置的 comparer 方法,这些方法应该可以满足 computed 的 equals 选项的大多数需求。

  • comparer.identity 使用恒等(===)运算符来确定两个值是否相同。
  • comparer.default 与 comparer.identity 相同,但同时也认为 NaN 等于 NaN。
  • comparer.structural 执行深度结构化比较来确定两个值是否相同。
  • comparer.shallow 执行浅层结构化比较来确定两个值是否相同。

您可以从 mobx 中导入 comparer 来访问这些方法。 它们也可以用于 reaction。

requiresReaction

建议将此设置为非常昂贵的计算值上的 true。 如果你试图在非反应式上下文中读取它的值,在这种情况下它可能没有被缓存,它会导致计算抛出错误,而不是执行昂贵的重新评估。

keepAlive

这避免了在计算值没有被任何东西观察到时(见上面的解释)将其挂起。 可能导致内存泄漏,类似于针对 reactions 的讨论。

← ActionsReactions {🚀} →
  • 例子
  • 规则
  • 提示
  • 选项 {🚀}
    • 名称
    • 相等
    • requiresReaction
    • keepAlive
MobX 🇺🇦
文档
关于 MobXMobX 的要点
社区
GitHub 讨论 (NEW)Stack Overflow
更多
星标