今天看项目看到这么一段
this._zone.run(() => { });
什么是 Zone? Zone 是一种用于拦截和跟踪异步工作的机制。
什么是 NgZone? Zone.js 将会对每一个异步操作创建一个 task。一个 task 运行于一个 Zone 中。通常来说, 在 Angular 应用中,每个 task 都会在 “Angular” Zone 中运行,这个 Zone 被称为 NgZone。一个 Angular 应用中只存在一个 Angular Zone,而变更检测只会由 运行于这个 NgZone 中的异步操作触发。
如何在代码中上手 NgZone? 先了解 run 与 runOutsideAngular 两个 API 即可。
以上涉及到两个 API,再加上一些常见用法。我们来看看在不同场景下,代码都该怎么写。
首先,函数 runOutsideAngular 用于确保代码于 NgZone 之外运行,即保证 Angular 的变更检测不会因为相关代码而触发。
在某些场景中因为自动变更检测触发过于频繁而导致性能下降时,你可能会希望可以自己掌握全局。
如果你仍然希望利用Zone.js的优势,但又想要控制能够触发变更检测的场景/动作,你可以采用如下方法。
通过注入 NgZone
, 你可以使用一个 API 决定”一个异步操作是否需要在 NgZone
中执行“。函数 runOutsideAngular 用于确保代码于 NgZone
之外运行,保证变更检测不会因为相关代码而触发。
这儿有一个例子:
constructor(private ngZone: NgZone) {
this.ngZone.runOutsideAngular(() => {
// this will not trigger change detection
setInterval(() => doSomething(), 100)
});
}
constructor(private ngZone: NgZone) {
this.ngZone.runOutsideAngular(() => {
// 以下代码不会触发变更检测
setInterval(() => doSomething(), 100)
});
}
run 方法的目的与 runOutsideAngular 正好相反:任何写在 run 里的方法,都会进入 Angular Zone 的管辖范围。(组里大佬拿来强制更新Angular的视图,学到了)
// 此处的 num 会实时更新
import { Component, NgZone } from '@angular/core';
@Component({
selector: 'my-app',
template: `
<p>
<label>Count: </label>
</p>
`
})
export class AppComponent {
num = 0;
constructor(private zone: NgZone) {
this.zone.runOutsideAngular(() => {
let i = 0;
const token = setInterval(() => {
this.zone.run(() => {
this.num = ++i;
})
console.log(this.num);
if (i == 10) {
clearInterval(token);
}
}, 1000);
})
}
}
RxJS 与 Zone.js 如何一起使用
默认情况下,RxJS 与 Zone.js 可能不会如你预期的那般运行。比如,你可能会把 Observable 相关代码封装进一个函数,并将其传递到 NgZone.runOutsideAngular
方法中,却仍然以在NgZone 内部运行的任务结束这个 Observable(与 Zone 外创建数据流,于 Zone 内订阅数据流)。
使用 RxJS 时,应始终从Zone.js导入相关补丁,以确保 RxJS 以”合理“的方式工作。在 subscription,operator 或 Observable 构造时,它会记住当前所处的 Zone,并且始终在该 Zone 中运行,这与你订阅的 Zone 无关,两者完全独立。
你可以依照如下的方案给 Zone.js 和 RxJS 的适配打补丁,通常在 polyfill.ts
文件中。
import ‘zone.js/dist/zone-patch-rxjs’;
插个链接,需要重点看一下这系列的文章。
=================================split=====================================
Subject?
每个 Subject 都是 Observable
每个 Subject 都是Observer
我们为 Subject 添加了两个观察者,然后给 Subject 提供一些值:
var subject = new Rx.Subject();
subject.subscribe({
next: (v) => console.log('observerA: ' + v)
});
subject.subscribe({
next: (v) => console.log('observerB: ' + v)
});
subject.next(1);
subject.next(2);
下面是控制台的输出:
observerA: 1
observerB: 1
observerA: 2
observerB: 2
因为 Subject 是观察者,这也就在意味着你可以把 Subject 作为参数传给任何 Observable 的 subscribe
方法,如下面的示例所展示的:
var subject = new Rx.Subject();
subject.subscribe({
next: (v) => console.log('observerA: ' + v)
});
subject.subscribe({
next: (v) => console.log('observerB: ' + v)
});
var observable = Rx.Observable.from([1, 2, 3]);
observable.subscribe(subject); // 你可以提供一个 Subject 进行订阅
执行结果:
observerA: 1
observerB: 1
observerA: 2
observerB: 2
observerA: 3
observerB: 3
使用上面的方法,我们基本上只是通过 Subject 将单播的 Observable 执行转换为多播的。这也说明了 Subjects 是将任意 Observable 执行共享给多个观察者的唯一方式。
还有一些特殊类型的 Subject
:BehaviorSubject
、ReplaySubject
和 AsyncSubject
。
这三个变体功能及其强大!一定要学会!