跳到主要内容

拦截与观察(Intercept & Observe) {🚀}

⚠️ 警告: Intercept 和 Observe是低等级方法,,他们不应该被应用在正式的开发中。可以考虑使用 reaction,因为 observe 不遵循事务原则也不支持对深层级的对象变动的监听。这种方法是反面模式(anti-pattern)。如果你想使用 observe来获取新值和旧的值,请考虑优先使用reaction。 ⚠️

observeintercept 可以用来监视单个可被观察的对象的变化,但是它们 不适合 用来跟踪深层级的可观察对象。

  • intercept 可用于在对象在发生变更之前进行拦截,从而做到验证修改,统一变更,取消变更(validating, normalizing or cancelling)。
  • observe 允许你在数据发生更改后观察到更改。

拦截(Intercept)

方法声明: intercept(target, propertyName?, interceptor)

请避免使用此API。 大体上讲它提供了一种面向切面编程的方案(aspect-oriented programming),它是在数据流任何更新状态 之前 (而不是在更新过程中)进行数据验证,所以很难进行调试。

  • target: 可被拦截到的对象。
  • propertyName: 可选参数,用于指定要拦截的特定的对象属性。请注意,这里intercept(user.name, interceptor)intercept(user, "name", interceptor)有本质上的不同。第一种尝试将拦截添加到 当前的 value而不是 user.name,这可能导致 user.name 的变化根本无法被拦截到。后者则是对username 属性进行拦截。
  • interceptor: 回调函数,在可观察对象 每次 发生变更都会被调用。入参是一个描述具体发生了什么变更的对象。

intercept 必须告诉MobX如何对当前要发生的变更进行处理。因此,它应该具有下列操作中的一种:

  1. 直接返回回调函数的入参change对象,这种情况下发生的变更会被接受。
  2. 修改回调函数的入参 change 对象并返回它, 比如你可以格式化数据。并非所有的数据都是可以修改的,可以参照下面的代码样例。
  3. 返回 null, 这意味着变更不会发生,而是会被忽略,这是一个非常强大的概念。他意味着你可以让你的对象变得完全不可变(immutable)。
  4. 当发生的变更不符合你的要求的时候,你可以抛出一些异常。

这个方法会返回一个disposer 函数,你可以调用这个方法来注销拦截。 有可能你会注册多个拦截 intercept到一个可观察的对象上,他们将会被按照注册的顺序链式调用。 如果这些拦截中的一个返回 null 或者抛出异常, 那么剩下的拦截 intercept将不会被继续触发。 你有可能同时在父对象(即原型·译者注)和私有属性(实例属性·译者注)上同时注册了拦截,那么拦截 intercept会先执行注册在父对象上的,再执行私有属性上的。

const theme = observable({
backgroundColor: "#ffffff"
})

const disposer = intercept(theme, "backgroundColor", change => {
if (!change.newValue) {
// 忽略未设置的颜色变更。
return null
}
if (change.newValue.length === 6) {
// 增加忽略的修饰前缀 `#`。
change.newValue = "#" + change.newValue
return change
}
if (change.newValue.length === 7) {
// oh 这是一个被格式化好的颜色代码!
return change
}
if (change.newValue.length > 10) {
// 注销拦截。
disposer()
}
throw new Error("哦我的上帝啊,这看起来不像是一种颜色: " + change.newValue)
})

观察(Observe)

方法声明: observe(target, propertyName?, listener, invokeImmediately?)

参阅上述声明, 请避免使用此API 并改用 reaction

  • target: 可被观察到的对象。
  • propertyName: 可选参数,用于指定要观察的特定的对象属性。请注意,这里 observe(user.name, listener)observe(user, "name", listener)有本质上的不同, 第一种尝试将监听添加到 当前的 value 而不是 user.name, 这可能导致user.name 的变化根本无法被监听到。因为后者是对username属性进行监听。
  • listener: 回调函数,在可观察对象 每次 发生变更都会被调用。入参是一个描述具体发生了什么变更的对象。包装对象(boxed observables)除外,它会调用回调函数的两个参数: newValue, oldValue
  • invokeImmediately: 默认值为 false 。如果你想让监听 observelistener回调函数立即执行, 而不是等待观察到第一次变化后触发, 可以将它设置为 true 。 目前(现在)所有的观察对象类型都不支持。

这个方法会返回一个disposer 函数,你可以调用这个方法来注销监听器。 请注意,transaction 不会影响observe方法的工作。 这意味着即使在事务内部,observe也会针对每个变更触发监听。因此,通常可以使用更健壮的和声明类型的 autorun替代observe方法。

observe 会在他们发生 变更(mutations) 时产生响应, autorun or reaction 会对 新值(new values) 产生时做出响应。 大多数情况下,这就足够了。

例子:

import { observable, observe } from "mobx"

const person = observable({
firstName: "Maarten",
lastName: "Luther"
})

// 观察所有字段.
const disposer = observe(person, change => {
console.log(change.type, change.name, "from", change.oldValue, "to", change.object[change.name])
})

person.firstName = "Martin"
// 打印: 'update firstName from Maarten to Martin'

// 注销观察.
disposer()

// 监听单个字段.
const disposer2 = observe(person, "lastName", change => {
console.log("LastName changed to ", change.newValue)
})

相关文章: Object.observe已死,mobx.observe当立(Object.observe is dead. Long live mobx.observe)

事件概览 (Event overview)

interceptobserve 会接受一个事件对象至少包含以下属性:

  • object: 可观察到的触发事件。
  • debugObjectName: 可观察到的触发事件的名称 (为调试准备)。
  • observableKind: 可观察的对象类型 (value, set, array, object, map, computed)。
  • type (string):当前的事件类型。

这些是每种类型其他可用的字段:

可观察的对象类型事件类型属性描述在拦截阶段是否可用在拦截阶段是否可被修改
Objectaddname要添加的属性的名称
newValue将要变更成为的新值
update*name要添加的属性的名称
newValue将要变更成为的新值
oldValue将被替换的旧值
Arrayspliceindex通过迭代器触发索引变化的方法, 包括 push, unshift, replace, 等
removedCount被删除的数量统计
added已经添加的数组元素
removed被移除的数组元素
addedCount添加的元素数量
updateindex被更新的索引
newValue将会被合并的新值
oldValue将会被取代的旧值
Mapaddname添加的元素的名称
newValue被添加的新值
updatename正在更新的元素名称
newValue被更新的元素值
oldValue被更新取代的旧值
deletename被删除元素的名称
oldValue被删除元素的旧值
Boxed & computed observablescreatenewValue在创建过程中分配的值, 仅在boxed observables的 spy event 起效
updatenewValue将被合并的新值
oldValue可观察的旧值

注意: 对象的 update 事件不会触发更新(computed),他们不是变更 (mutations)。但是可以通过使用指定observe监听特定属性来监听它们的改变:observe(object, 'computedPropertyName', listener)