跟踪器
Meteor 拥有一个简单的依赖项跟踪系统,允许它在Session
变量、数据库查询和其他数据源发生更改时自动重新运行模板和其他计算。
与大多数其他系统不同,您无需手动声明这些依赖项,它“即可运行”。该机制简单高效。当您调用支持响应式更新的函数(例如数据库查询)时,它会自动保存当前的计算对象(如果有)(例如,表示当前正在渲染的模板)。稍后,当数据更改时,该函数可以“使”计算无效,从而导致它重新运行(重新渲染模板)。
应用程序会发现Tracker.autorun
很有用,而更高级的功能(如Tracker.Dependency
和onInvalidate
回调)主要用于实现新的响应式数据源的包作者。
Tracker.autorun仅客户端
仅客户端
摘要
立即运行函数,并在其依赖项发生更改时稍后重新运行它。返回一个 Computation 对象,可用于停止或观察重新运行。
参数
源代码名称 | 类型 | 描述 | 必需 |
---|---|---|---|
runFunc | Tracker.ComputationFunction | 要运行的函数。它接收一个参数:将返回的 Computation 对象。 | 是 |
options | 对象 | 否 |
Tracker.autorun
允许您运行依赖于响应式数据源的函数,以便如果稍后数据发生更改,则该函数将重新运行。
例如,您可以监视游标(它是响应式数据源)并将其聚合到会话变量中
Tracker.autorun(() => {
const oldest = _.max(Monkeys.find().fetch(), (monkey) => {
return monkey.age;
});
if (oldest) {
Session.set('oldest', oldest.name);
}
});
或者,您可以等待会话变量具有某个值,并在第一次出现时执行某些操作,在计算上调用stop
以防止进一步重新运行
Tracker.autorun((computation) => {
if (!Session.equals('shouldAlert', true)) {
return;
}
computation.stop();
alert('Oh no!');
});
该函数立即被调用,此时如果shouldAlert
已为真,它可能会发出警报并立即停止。否则,当shouldAlert
变为真时,该函数将再次运行。
数据依赖项的更改不会导致立即重新运行,而是“使”计算无效,导致它在下一次刷新时重新运行。如果存在无效的计算,则系统空闲后会自动发生刷新。您还可以使用Tracker.flush
强制立即刷新所有挂起的重新运行。
如果嵌套调用Tracker.autorun
,则当外部调用停止或重新运行时,内部调用将自动停止。订阅和观察者在用作重新运行的计算的一部分时也会自动停止,允许建立新的订阅和观察者。有关订阅和响应式的更多信息,请参阅Meteor.subscribe
。
如果 autorun 的初始运行引发异常,则计算会自动停止,并且不会重新运行。
Tracker.autorun 和异步回调
Tracker.autorun
可以接受一个async
回调函数。为了保留异步回调函数内响应式变量的响应性,您必须使用如下所述的Tracker.withComputation
调用
Tracker.withComputation
摘要
帮助函数,使跟踪器能够与 Promise 一起使用。
参数
源代码名称 | 类型 | 描述 | 必需 |
---|---|---|---|
computation | computation | 已跟踪的计算 | 是 |
func | func | 需要调用的异步函数,并且需要是响应式的 | 是 |
import { Tracker } from "meteor/tracker";
Tracker.withComputation(
Computation,
() => {},
);
Tracker.autorun(async function example1(computation) {
// Code before the first await will stay reactive.
reactiveVar1.get(); // This will trigger a rerun.
let links = await LinksCollection.findAsync({}).fetch(); // First async call will stay reactive.
// Code after the first await looses Tracker.currentComputation: no reactivity.
reactiveVar2.get(); // This won't trigger a rerun.
// You can bring back reactivity with the Tracker.withCompuation wrapper:
let users = await Tracker.withComputation(computation, () => Meteor.users.findAsync({}).fetch());
// Code below will again not be reactive, so you will need another Tracker.withComputation.
const value = Tracker.withComputation(computation, () => reactiveVar3.get()); // This will trigger a rerun.
});
根据经验法则,您可以将所有响应式语句包装在Tracker.withComputation
中以保留当前计算。但这会带来性能成本——应仅在需要时使用。
其背后的原因是,await 会隐式地“移动”下面的代码到已解析的 Promise 函数中。当此函数运行(从微任务队列中获取后),Tracker.withComputation
会保留对Tracker.autorun
计算的引用。
react-meteor-data
包使用Tracker.withComputation
使useTracker
能够接受异步回调。更多内容请参见此处
在 Meteor 2.10 之前的版本中使用异步回调
Tracker.autorun
可以接受一个async
回调函数。但是,异步回调函数仅依赖于在任何返回 Promise 的已调用函数之前调用的响应式函数。
示例 1 - autorun example1()
不依赖于对Meteor.users
集合的响应式更改。因为它不依赖于任何响应式内容,所以它只会运行一次
Tracker.autorun(async function example1() {
let asyncData = await asyncDataFunction();
let users = Meteor.users.find({}).fetch();
});
但是,只需更改顺序,以便在对Meteor.users.find
的响应式调用之前没有async
调用,就会使异步 autorun example2()
依赖于对Meteor.users
集合的响应式更改。
示例 2 - autorun example2()
确实依赖于对 Meteor.users 集合的响应式更改。对Meteor.users
集合的更改将导致example2()
重新运行
Tracker.autorun(async function example2() {
let users = Meteor.users.find({}).fetch();
let asyncData = await asyncDataFunction();
});
Tracker.flush仅客户端
仅客户端
摘要
立即处理所有响应式更新,并确保所有无效的计算都重新运行。
import { Tracker } from "meteor/tracker";
Tracker.flush();
通常,当您进行更改(例如写入数据库)时,其影响(例如更新 DOM)会延迟到系统空闲时。这使事情变得可预测——您可以知道 DOM 不会在您的代码运行时发生更改。这也是使 Meteor 速度更快的原因之一。
Tracker.flush
强制所有挂起的响应式更新完成。例如,如果事件处理程序更改了将导致用户界面的一部分重新渲染的 Session 变量,则处理程序可以调用flush
以立即执行重新渲染,然后访问生成的 DOM。
系统空闲时会发生自动刷新,其执行的工作与Tracker.flush
完全相同。刷新过程包括重新运行任何无效的计算。如果在刷新期间发生其他无效操作,则它们作为同一刷新的一部分进行处理,直到没有更多工作要完成。在处理未完成的无效操作后,将调用使用Tracker.afterFlush
注册的回调。
从刷新内部或正在运行的计算中调用flush
是非法的。
该跟踪器手册描述了刷新周期的动机以及Tracker.flush
和Tracker.afterFlush
做出的保证。
Tracker.nonreactive仅客户端
仅客户端
摘要
在不跟踪依赖项的情况下运行函数。
参数
源代码名称 | 类型 | 描述 | 必需 |
---|---|---|---|
func | 函数 | 要立即调用的函数。 | 是 |
import { Tracker } from "meteor/tracker";
Tracker.nonreactive(
() => {}
);
调用func
,同时将Tracker.currentComputation
临时设置为null
,并返回func
自己的返回值。如果func
访问响应式数据源,则这些数据源永远不会导致封闭计算重新运行。
Tracker.active仅客户端
仅客户端
摘要
如果存在当前计算,则为真,这意味着对响应式数据源的依赖项将被跟踪,并可能导致当前计算重新运行。
import { Tracker } from 'meteor/tracker';
/** @type {Boolean} */
if (Tracker.active) {
// do something
}
此值对于数据源实现来说很有用,可以确定它们是被响应式地访问还是没有被响应式地访问。
Tracker.inFlush仅客户端
仅客户端
摘要
如果我们现在正在计算计算,无论是第一次还是重新计算,则为真。这与 Tracker.active 相匹配,除非我们在 Tracker.nonreactive 内部,后者即使封闭计算可能仍在运行也会使 currentComputation 为空。
import { Tracker } from "meteor/tracker";
/** @returns Boolean */
const result = Tracker.inFlush();
此值指示刷新是否正在进行。
Tracker.currentComputation仅客户端
仅客户端
摘要
当前计算,或者如果没有则为null
。当前计算是由对Tracker.autorun
的最内部活动调用创建的Tracker.Computation
对象,并且它是当访问响应式数据源时获得依赖项的计算。
很少需要直接访问currentComputation
。当前计算由Tracker.active
(它测试是否存在一个)、dependency.depend()
(它注册它依赖于一个依赖项)和Tracker.onInvalidate
(它向其注册一个回调)隐式使用。
Tracker.onInvalidate仅客户端
仅客户端
摘要
在当前计算(必须存在)上注册一个新的 onInvalidate
回调函数,当当前计算失效或停止时立即调用。
参数
源代码名称 | 类型 | 描述 | 必需 |
---|---|---|---|
回调函数 | 函数 | 一个将被调用为 | 是 |
import { Tracker } from "meteor/tracker";
Tracker.onInvalidate(
() => {}
);
更多详细信息,请参阅 computation.onInvalidate
。
Tracker.afterFlush仅客户端
仅客户端
摘要
安排一个函数在下次刷新期间调用,或者如果当前正在进行刷新,则在当前刷新的稍后时间调用,在所有失效的计算重新运行之后。该函数将运行一次,除非再次调用 afterFlush
,否则不会在后续刷新中运行。
参数
源代码名称 | 类型 | 描述 | 必需 |
---|---|---|---|
回调函数 | 函数 | 在刷新时调用的函数。 | 是 |
import { Tracker } from "meteor/tracker";
Tracker.afterFlush(
() => {}
);
保证通过多次调用 afterFlush
安排的函数按照 afterFlush
被调用的顺序运行。保证函数在没有需要重新运行的失效计算的时间点被调用。这意味着,如果 afterFlush
函数使计算失效,则该计算将在任何其他 afterFlush
函数被调用之前重新运行。
Tracker.Computation
计算对象表示响应式数据更改而重复运行的代码。计算没有返回值;它们只是执行操作,例如在屏幕上重新渲染模板。计算是使用 Tracker.autorun
创建的。使用 stop
阻止计算进一步重新运行。
每次计算运行时,它可能会访问各种用作计算输入的响应式数据源,这些数据源称为其依赖项。在未来的某个时间点,这些依赖项之一可能会通过使计算失效来触发计算重新运行。发生这种情况时,依赖项将被清除,并且计算将被安排在刷新时重新运行。
当前计算 (Tracker.currentComputation
) 是当前正在运行或重新运行(计算)的计算,以及在访问响应式数据源时获得依赖项的计算。数据源负责使用 Tracker.Dependency
对象跟踪这些依赖项。
使计算失效会将其 invalidated
属性设置为 true,并立即调用计算的所有 onInvalidate
回调函数。当发生刷新时,如果计算已失效且未停止,则通过将 invalidated
属性设置为 false
并调用传递给 Tracker.autorun
的原始函数来重新运行计算。当当前代码运行结束时,或如果调用了 Tracker.flush
,则会发生刷新。
停止计算会使它失效(如果它有效)以用于调用回调函数,但确保它永远不会被重新运行。
示例
// If we're in a computation, then perform some clean-up when the current
// computation is invalidated (rerun or stopped).
if (Tracker.active) {
Tracker.onInvalidate(() => {
x.destroy();
y.finalize();
});
}
computation.stop仅客户端
仅客户端
摘要
阻止此计算重新运行。
// computation is an instance of Tracker.Computation
computation.stop();
停止计算是不可逆的,并保证它永远不会被重新运行。您可以在任何时间停止计算,包括从计算自己的运行函数中停止。停止已经停止的计算没有任何效果。
停止计算会导致其 onInvalidate
回调函数立即运行(如果它当前未失效),以及其 stop
回调函数。
嵌套计算在它们的封闭计算重新运行时会自动停止。
computation.invalidate仅客户端
仅客户端
摘要
使此计算失效,以便它将被重新运行。
// computation is an instance of Tracker.Computation
computation.invalidate();
使计算失效会将其标记为在 刷新时 重新运行,此时计算再次变为有效。手动使计算失效的情况很少见,因为响应式数据源在发生更改时会使它们的调用计算失效。响应式数据源依次使用一个或多个 Tracker.Dependency
对象执行此失效操作。
使计算失效会立即调用在其上注册的所有 onInvalidate
回调函数。使当前已失效或已停止的计算失效没有任何效果。计算可以使自身失效,但如果它无限期地继续这样做,结果将是无限循环。
computation.onInvalidate仅客户端
仅客户端
摘要
注册 callback
以在下次使此计算失效时运行,或者如果计算已失效则立即运行它。回调函数只运行一次,并且在计算再次变为有效后,除非再次调用 onInvalidate
,否则不会在将来的失效时运行。
参数
源代码名称 | 类型 | 描述 | 必需 |
---|---|---|---|
回调函数 | 函数 | 在失效时要调用的函数。接收一个参数,即失效的计算。 | 是 |
// computation is an instance of Tracker.Computation
computation.onInvalidate(
() => {}
);
onInvalidate
注册一个一次性回调函数,该回调函数要么立即触发,要么在计算下次失效或停止时触发。它由响应式数据源用来在重新运行或停止计算时清理资源或断开依赖关系。
要在计算重新计算后获得回调,您可以从 onInvalidate
中调用 Tracker.afterFlush
。
computation.onStop仅客户端
仅客户端
摘要
注册 callback
以在停止此计算时运行,或者如果计算已停止则立即运行它。回调函数在任何 onInvalidate
回调函数之后运行。
参数
源代码名称 | 类型 | 描述 | 必需 |
---|---|---|---|
回调函数 | 函数 | 在停止时要调用的函数。接收一个参数,即已停止的计算。 | 是 |
// computation is an instance of Tracker.Computation
computation.onStop(
() => {}
);
computation.invalidated仅客户端
仅客户端
摘要
如果此计算已失效(但尚未重新运行),或如果它已停止,则为 true。
import { Tracker.Computation } from 'meteor/tracker';
/** @type {Boolean} */
if (Tracker.Computation#invalidated) {
// do something
}
此属性最初为 false。它由 stop()
和 invalidate()
设置为 true。当计算在刷新时重新计算时,它将重置为 false。
computation.firstRun仅客户端
仅客户端
摘要
在调用 Tracker.autorun
时计算的初始运行期间为 true,在后续重新运行和其他时间为 false。
import { Tracker.Computation } from 'meteor/tracker';
/** @type {Boolean} */
if (Tracker.Computation#firstRun) {
// do something
}
此属性是为了支持常见模式而提供的便利,在这种模式下,计算具有特定于第一次运行的逻辑。
初始计算完成后,Computation.firstRunPromise
将设置为 autorun 函数调用的结果。如果 autorun 函数是异步函数,则它将包含其 promise,从而使执行的完成可等待。这使我们能够手动同步 autorun,如下所示
await Tracker.autorun(async () => {
await Meteor.userAsync();
(...more async code...)
}).firstRunPromise;
await Tracker.autorun(async () => {
await asyncSomeOrOther();
(...more async code...)
}).firstRunPromise;
为了获得更好的开发体验,firstRunPromise
会自动附加到您的异步 autorun
调用中,因此您不必自己编写它们。这意味着这也适用
await Tracker.autorun(async () => {
await Meteor.userAsync();
(...more async code...)
});
await Tracker.autorun(async () => {
await asyncSomeOrOther();
(...more async code...)
});
Tracker.Dependency
依赖项表示计算可能依赖的响应式数据的原子单元。诸如 Session 或 Minimongo 之类的响应式数据源在内部为不同的数据片段创建不同的依赖项对象,每个对象可能被多个计算依赖。当数据更改时,计算将失效。
依赖项不存储数据,它们只是跟踪如果某些内容发生更改需要失效的计算集。通常,数据值将伴随一个跟踪依赖于它的计算的依赖项对象,例如以下示例
let weather = 'sunny';
const weatherDep = new Tracker.Dependency();
function getWeather() {
weatherDep.depend();
return weather;
}
function setWeather(newWeather) {
weather = newWeather;
// Note: We could add logic here to only call `changed` if the new value is
// different from the old value.
weatherDep.changed();
}
此示例实现了一个天气数据源,它具有简单的 getter 和 setter。getter 使用 depend()
记录当前计算依赖于 weatherDep
依赖项,而 setter 通过调用 changed()
信号依赖项使所有依赖计算失效。
依赖项本身不存储数据的原因是可以将多个依赖项与同一数据片段关联。例如,一个依赖项可能表示数据库查询的结果,而另一个可能只表示结果中的文档数量。依赖项可以表示天气是否晴朗,或者温度是否在冰点以上。 Session.equals
以这种方式实现是为了提高效率。当您调用 Session.equals('weather', 'sunny')
时,当前计算将依赖于一个内部依赖项,如果天气从例如 rainy
变为 cloudy
,则该依赖项不会改变。
从概念上讲,依赖项可以执行的只有两件事:获得依赖项和更改。
依赖项的依赖计算始终有效(它们具有 invalidated === false
)。如果依赖项在任何时候失效,无论是由依赖项本身还是其他方式,它都会立即被移除。
请参阅 Tracker 手册,了解如何使用 Tracker.Dependency
创建响应式数据源。
dependency.changed仅客户端
仅客户端
摘要
立即使所有依赖计算失效并将其作为依赖项移除。
// dependency is an instance of Tracker.Dependency
dependency.changed();
dependency.depend仅客户端
仅客户端
摘要
声明当前计算(如果给出则为 fromComputation
)依赖于 dependency
。下次 dependency
更改时,计算将失效。
如果没有当前计算并且 depend()
被调用且没有参数,则它什么也不做并返回 false。
如果计算是 dependency
的新依赖项而不是现有依赖项,则返回 true。
参数
源代码名称 | 类型 | 描述 | 必需 |
---|---|---|---|
fromComputation | Tracker.Computation | 一个可选的计算,声明依赖于 | 否 |
// dependency is an instance of Tracker.Dependency
/** @returns Boolean */
const result = dependency.depend(
Computation
);
dependency.depend()
用于响应式数据源实现中,以记录当前计算正在访问 dependency
的事实。
dependency.hasDependents仅客户端
仅客户端
摘要
如果此依赖项具有一个或多个依赖计算,则为 true,如果此依赖项发生更改,则这些计算将失效。
// dependency is an instance of Tracker.Dependency
/** @returns Boolean */
const result = dependency.hasDependents();
对于创建许多内部依赖项的响应式数据源,此函数可用于确定特定依赖项是否仍在跟踪任何依赖关系,或者是否可以对其进行清理以节省内存。