拦截与观察 {🚀}
⚠️ 警告: 拦截和观察是低级实用程序,在实践中不应该需要。请改用某种形式的 反应,因为 observe
不尊重事务,也不支持对更改进行深度观察。使用这些实用程序是一种反模式。如果您打算使用 observe
获取旧值和新值,请改用 reaction
。 ⚠️
observe
和 intercept
可用于监视单个可观察对象的更改,但它们不跟踪嵌套的可观察对象。
intercept
可用于在将更改应用于可观察对象之前检测和修改更改(验证、规范化或取消)。observe
允许您在更改完成之后拦截更改。
拦截
用法: intercept(target, propertyName?, interceptor)
请避免使用此 API。它基本上提供了一些面向方面的编程,创建了非常难以调试的流程。相反,请在更新任何状态之前执行数据验证,而不是在更新过程中执行。
target
: 要保护的可观察对象。propertyName
: 可选参数,用于指定要拦截的特定属性。请注意,intercept(user.name, interceptor)
与intercept(user, "name", interceptor)
从根本上不同。前者尝试向user.name
内部的当前value
添加拦截器,该值可能根本不是可观察对象。后者拦截对user
的name
属性的更改。interceptor
: 对对可观察对象进行的每次更改都调用的回调。接收一个描述更改的单一更改对象。
intercept
应该告诉 MobX 当前更改需要发生什么。因此它应该执行以下操作之一
- 按原样从函数中返回接收到的
change
对象,在这种情况下将应用更改。 - 修改
change
对象并返回它,例如规范化数据。并非所有字段都可以修改,见下文。 - 返回
null
,这表示可以忽略更改,并且不应应用。这是一个强大的概念,您可以使用它使您的对象暂时不可变。 - 抛出一个异常,例如,如果某些不变式没有满足。
该函数返回一个 disposer
函数,该函数可用于在调用时取消拦截器。可以为同一个可观察对象注册多个拦截器。它们将按照注册顺序链接。如果其中一个拦截器返回 null
或抛出异常,则其他拦截器将不再被评估。也可以在父对象和单个属性上注册拦截器。在这种情况下,父对象拦截器将在属性拦截器之前运行。
const theme = observable({
backgroundColor: "#ffffff"
})
const disposer = intercept(theme, "backgroundColor", change => {
if (!change.newValue) {
// Ignore attempts to unset the background color.
return null
}
if (change.newValue.length === 6) {
// Correct missing '#' prefix.
change.newValue = "#" + change.newValue
return change
}
if (change.newValue.length === 7) {
// This must be a properly formatted color code!
return change
}
if (change.newValue.length > 10) {
// Stop intercepting future changes.
disposer()
}
throw new Error("This doesn't look like a color at all: " + change.newValue)
})
观察
用法: observe(target, propertyName?, listener, invokeImmediately?)
请参阅上面的通知,请避免使用此 API,而改用 reaction
。
target
: 要观察的可观察对象。propertyName
: 可选参数,用于指定要观察的特定属性。请注意,observe(user.name, listener)
与observe(user, "name", listener)
从根本上不同。前者观察user.name
内部的当前value
,该值可能根本不是可观察对象。后者观察user
的name
属性。listener
: 对对可观察对象进行的每次更改都调用的回调。接收一个描述更改的单一更改对象,但对于封装的可观察对象,它将使用两个参数调用listener
:newValue, oldValue
。invokeImmediately
: 默认情况下为 false。如果希望observe
直接使用可观察对象的状态调用listener
,而不是等待第一个更改,请将其设置为 true。并非所有类型的可观察对象都支持(尚未)。
该函数返回一个 disposer
函数,该函数可用于取消观察者。请注意,transaction
不影响 observe
方法的运行。这意味着即使在事务中,observe
也将为每个更改触发其监听器。因此,autorun
通常是 observe
更强大且更具声明性的替代方案。
observe
在进行更改时对更改做出反应,而像 autorun
或 reaction
这样的反应在更改可用时对新值做出反应。在许多情况下,后者就足够了。
示例
import { observable, observe } from "mobx"
const person = observable({
firstName: "Maarten",
lastName: "Luther"
})
// Observe all fields.
const disposer = observe(person, change => {
console.log(change.type, change.name, "from", change.oldValue, "to", change.object[change.name])
})
person.firstName = "Martin"
// Prints: 'update firstName from Maarten to Martin'
// Ignore any future updates.
disposer()
// Observe a single field.
const disposer2 = observe(person, "lastName", change => {
console.log("LastName changed to ", change.newValue)
})
相关博客: Object.observe is dead. Long live mobx.observe
事件概述
intercept
和 observe
的回调将接收一个事件对象,该对象至少具有以下属性
object
: 触发事件的可观察对象。debugObjectName
: 触发事件的可观察对象的名称(用于调试)。observableKind
: 可观察对象的类型(值、集合、数组、对象、映射、计算属性)。type
(string): 当前事件的类型。
以下是每种类型可用的其他字段
可观察对象类型 | 事件类型 | 属性 | 描述 | 拦截期间可用 | 可以被拦截修改 |
---|---|---|---|---|---|
对象 | 添加 | name | 正在添加的属性的名称。 | √ | |
newValue | 正在分配的新值。 | √ | √ | ||
更新* | name | 正在更新的属性的名称。 | √ | ||
newValue | 正在分配的新值。 | √ | √ | ||
oldValue | 被替换的值。 | ||||
数组 | splice | index | splice 的起始索引。splice 也会被 push 、unshift 、replace 等触发。 | √ | |
removedCount | 正在移除的项目数量。 | √ | √ | ||
added | 正在添加的项目数组。 | √ | √ | ||
removed | 已被移除的项目数组。 | ||||
addedCount | 已被添加的项目数量。 | ||||
更新 | index | 正在更新的单个条目的索引。 | √ | ||
newValue | 将要分配的 newValue。 | √ | √ | ||
oldValue | 已被替换的 oldValue。 | ||||
映射 | 添加 | name | 已添加的条目的名称。 | √ | |
newValue | 正在分配的新值。 | √ | √ | ||
更新 | name | 正在更新的条目的名称。 | √ | ||
newValue | 正在分配的新值。 | √ | √ | ||
oldValue | 已被替换的值。 | ||||
删除 | name | 正在删除的条目的名称。 | √ | ||
oldValue | 已被移除的条目的值。 | ||||
封装的可观察对象和计算属性 | 创建 | newValue | 创建期间分配的值。仅封装的可观察对象作为 spy 事件可用。 | ||
更新 | newValue | 正在分配的新值。 | √ | √ | |
oldValue | 可观察对象的前一个值。 |
注意: 对象 update
事件不会为更新的计算属性触发(因为它们不是更改)。但是,可以通过使用 observe(object, 'computedPropertyName', listener)
明确订阅特定属性来观察它们。