拦截和观察 {🚀}
¥Intercept & Observe {🚀}
⚠️ 警告:拦截和观察是底层实用程序,在实践中不应该需要。请改用某种形式的 reaction,因为 observe
不尊重事务,也不支持深入观察变化。使用这些实用程序是一种反模式。如果你打算使用 observe
访问旧值和新值,请改用 reaction
。⚠️
¥⚠️ Warning: intercept and observe are low level utilities, and should not be needed in practice. Use some form of reaction instead, as observe
doesn't respect transactions and doesn't support deep observing of changes. Using these utilities is an anti-pattern. If you intend to get access to the old and new value using observe
, use reaction
instead. ⚠️
observe
和 intercept
可用于监视单个可观察量的变化,但它们不跟踪嵌套可观察量。
¥observe
and intercept
can be used to monitor the changes of a single observable, but they don't track nested observables.
intercept
可用于在将突变应用于可观察值(验证、标准化或取消)之前检测和修改突变。¥
intercept
can be used to detect and modify mutations before they are applied to the observable (validating, normalizing or cancelling).observe
允许你在进行更改后拦截更改。¥
observe
allows you to intercept changes after they have been made.
拦截
¥Intercept
用法:intercept(target, propertyName?, interceptor)
¥Usage: intercept(target, propertyName?, interceptor)
请避免使用此 API。它基本上提供了一些面向方面的编程,创建了很难调试的流程。相反,在更新任何状态之前而不是在更新期间执行数据验证之类的操作。
¥Please avoid this API. It basically provides a bit of aspect-oriented programming, creating flows that are really hard to debug. Instead, do things like data validation before updating any state, rather than during.
target
:要守护的可观察物。¥
target
: the observable to guard.propertyName
:可选参数指定要拦截的特定属性。请注意,intercept(user.name, interceptor)
与intercept(user, "name", interceptor)
根本不同。第一个尝试在user.name
内的当前value
中添加一个拦截器,这可能根本不是可观察的。后者拦截对user
的name
属性的更改。¥
propertyName
: optional parameter to specify a specific property to intercept. Note thatintercept(user.name, interceptor)
is fundamentally different fromintercept(user, "name", interceptor)
. The first tries to add an interceptor to the currentvalue
insideuser.name
, which might not be an observable at all. The latter intercepts changes to thename
property ofuser
.interceptor
:对可观察对象进行的每次更改都会调用回调。接收描述突变的单个更改对象。¥
interceptor
: callback that is invoked for each change that is made to the observable. Receives a single change object describing the mutation.
intercept
应该告诉 MobX 当前的更改需要发生什么。因此它应该执行以下操作之一:
¥The intercept
should tell MobX what needs to happen with the current change.
Therefore it should do one of the following things:
从函数中按原样返回接收到的
change
对象,在这种情况下将应用突变。¥Return the received
change
object as-is from the function, in which case the mutation will be applied.修改
change
对象并返回它,例如标准化数据。并非所有字段都可以修改,请参见下文。¥Modify the
change
object and return it, for example to normalize the data. Not all fields are modifiable, see below.返回
null
,这表明可以忽略更改并且不应应用更改。这是一个强大的概念,例如,你可以使用它使对象暂时不可变。¥Return
null
, this indicates that the change can be ignored and shouldn't be applied. This is a powerful concept with which you can for example make your objects temporarily immutable.例如,如果不满足某些不变量,则抛出异常。
¥Throw an exception, if for example some invariant isn't met.
该函数返回一个 disposer
函数,可用于在调用时取消拦截器。可以将多个拦截器注册到同一个可观察对象。它们将按照注册顺序被链接起来。如果其中一个拦截器返回 null
或抛出异常,则其他拦截器将不再被评估。还可以在父对象和单个属性上注册拦截器。在这种情况下,父对象拦截器在属性拦截器之前运行。
¥The function returns a disposer
function that can be used to cancel the interceptor when invoked.
It is possible to register multiple interceptors to the same observable.
They will be chained in registration order.
If one of the interceptors returns null
or throws an exception, the other interceptors won't be evaluated anymore.
It is also possible to register an interceptor both on a parent object and on an individual property.
In that case the parent object interceptors are run before the property interceptors.
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
用法:observe(target, propertyName?, listener, invokeImmediately?)
¥Usage: observe(target, propertyName?, listener, invokeImmediately?)
请参阅上面的通知,请避免使用此 API 并使用 reaction
代替。
¥See above notice, please avoid this API and use reaction
instead.
target
:观察到的东西。¥
target
: the observable to observe.propertyName
:可选参数来指定要观察的特定属性。请注意,observe(user.name, listener)
与observe(user, "name", listener)
根本不同。第一个观察user.name
内当前的value
,这可能根本不是可观察的。后者观察user
的name
性质。¥
propertyName
: optional parameter to specify a specific property to observe. Note thatobserve(user.name, listener)
is fundamentally different fromobserve(user, "name", listener)
. The first observes the currentvalue
insideuser.name
, which might not be an observable at all. The latter observes thename
property ofuser
.listener
:对可观察对象进行的每次更改都会调用回调。接收描述突变的单个更改对象,盒装可观察值除外,它将使用两个参数调用listener
:newValue, oldValue
。¥
listener
: callback that will be invoked for each change that is made to the observable. Receives a single change object describing the mutation, except for boxed observables, which will invoke thelistener
with two parameters:newValue, oldValue
.invokeImmediately
:默认为 false。如果你希望observe
使用可观察对象的状态直接调用listener
,而不是等待第一次更改,请将其设置为 true。尚未得到所有类型的可观察量的支持。¥
invokeImmediately
: false by default. Set it to true if you wantobserve
to invoke thelistener
directly with the state of the observable, instead of waiting for the first change. Not supported (yet) by all kinds of observables.
该函数返回一个 disposer
函数,可用于取消观察者。请注意,transaction
不会影响 observe
方法的工作。这意味着即使在事务 observe
内部,也会为每个突变触发其监听器。因此,autorun
通常是 observe
的更强大、更具声明性的替代方案。
¥The function returns a disposer
function that can be used to cancel the observer.
Note that transaction
does not affect the working of the observe
method(s).
This means that even inside a transaction observe
will fire its listeners for each mutation.
Hence autorun
is usually a more powerful and declarative alternative to observe
.
observe
在突变发生时对其做出反应,而像 autorun
或 reaction
这样的反应则在新值可用时对其做出反应。在许多情况下,后者就足够了。
¥observe
reacts to mutations when they are being made, while reactions like autorun
or reaction
react to new values when they become available. In many cases the latter is sufficient.
示例:
¥Example:
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 已死。mobx.observe 万岁
¥Related blog: Object.observe is dead. Long live mobx.observe
事件概览
¥Event overview
intercept
和 observe
的回调将接收一个事件对象,该事件对象至少具有以下属性:
¥The callbacks of intercept
and observe
will receive an event object which has at least the following properties:
object
:触发事件的可观察值。¥
object
: the observable triggering the event.debugObjectName
:触发事件的可观察对象的名称(用于调试)。¥
debugObjectName
: the name of the observable triggering the event (for debugging).observableKind
:可观察对象的类型(值、集合、数组、对象、映射、计算)。¥
observableKind
: the type of the observable (value, set, array, object, map, computed).type
(字符串):当前事件的类型。¥
type
(string): the type of the current event.
这些是每种类型可用的附加字段:
¥These are the additional fields that are available per type:
可观察类型 | 事件类型 | 属性 | 描述 | 拦截期间可用 | 可以通过拦截来修改 |
---|---|---|---|---|---|
目的 | add | name | 正在添加的属性的名称。 | √ | |
newValue | 正在分配的新值。 | √ | √ | ||
更新* | name | 正在更新的属性的名称。 | √ | ||
newValue | 正在分配的新值。 | √ | √ | ||
oldValue | 被替换的值。 | ||||
数组 | splice | index | 拼接的起始索引。接头也由 push 、unshift 、replace 等触发。 | √ | |
removedCount | 被移除的项目数量。 | √ | √ | ||
added | 正在添加项目的数组。 | √ | √ | ||
removed | 包含已删除项目的数组。 | ||||
addedCount | 添加的项目数量。 | ||||
update | index | 正在更新的单个条目的索引。 | √ | ||
newValue | 将分配的 newValue。 | √ | √ | ||
oldValue | 被替换的旧值。 | ||||
映射 | add | name | 添加的条目的名称。 | √ | |
newValue | 正在分配的新值。 | √ | √ | ||
update | name | 正在更新的条目的名称。 | √ | ||
newValue | 正在分配的新值。 | √ | √ | ||
oldValue | 已被替换的值。 | ||||
delete | name | 正在删除的条目的名称。 | √ | ||
oldValue | 已删除条目的值。 | ||||
盒装和计算的可观测值 | create | newValue | 创建期间分配的值。仅可用作盒装可观察量的 spy 事件。 | ||
update | newValue | 正在分配的新值。 | √ | √ | |
oldValue | 可观察到的先前值。 |
注意:对象 update
事件不会因更新的计算值而触发(因为这些不是突变)。但可以通过使用 observe(object, 'computedPropertyName', listener)
显式订阅特定属性来观察它们。
¥Note: object update
events won't fire for updated computed values (as those aren't mutations). But it is possible to observe them by explicitly subscribing to the specific property using observe(object, 'computedPropertyName', listener)
.