NgZone
一种用于在 Angular Zone 内部或外部执行任务的可注入服务。
Zone.js 提供了一种称为区域(Zone)
的机制,用于封装和拦截浏览器中的异步活动。
最常见的用途是在启动包含一个或多个不需要 Angular 处理的 UI 更新或错误处理的异步任务的工作
时优化性能。
runOutsideAngular()
通过 runOutsideAngular()
运行函数可让你离开 Angular 的 Zone 并执行不会触发 Angular 变更检测或受 Angular 错误处理控制
的工作。
此函数中计划的任何将来的任务或微任务将在 Angular Zone 之外
继续执行。
使用 run()
重新进入 Angular Zone 并执行更新应用程序模型的工作。
run()
通过 run
运行的函数可让你从在 Angular Zone 之外执行的任务(通常通过 runOutsideAngular 启动)重新进入 Angular Zone 。
此函数中计划的任何将来的任务或微任务将在 Angular Zone 内
继续执行。
如果发生同步错误,它将被重新抛出,并且不会通过 onError 报告。
应用:减少变化检测次数,提高性能
Angular变化检测只会由运行于 NgZone 中的异步操作触发。会触发变化检测的行为有:
(1) 任何浏览器事件,比如click、keydown等。
(2) setInterval() 、setTimeout
(3) http请求
示例:
由于每次的keydown都会触发Angular的变化检测,而我们该示例中只需要按下Enter键时执行变化检测。
为了避免没有必要的变化检测,提高性能,我们将keydow事件包裹在runOutsideAngular中,让keydown事件执行在Zone之外;当按下的是Enter键时,再通过run方法重新回到Zone中即可。
@Directive({
selector: '[enter]'
})
export class MyEnterDirective implements OnInit {
@Output() enter = new EventEmitter();
constructor(private ngZone: NgZone, private elementRef: ElementRef<HTMLElement>) {}
ngOnInit(): void {
this.ngZone.runOutsideAngular(() => {
// 点击事件运行在Zone之外
this.elementRef.nativeElement.addEventListener('keydown', (event: KeyboardEvent) => {
const keyCode = event.which || event.keyCode;
if (keyCode === 13) {
// 当点击的是enter键时,再通过run重新进入Zone中
this.ngZone.run(() => {
this.enter.emit(event);
});
}
});
});
}
}
示例
// The example comes from the Angular official website
import {Component, NgZone} from '@angular/core';
import {NgIf} from '@angular/common';
@Component({
selector: 'ng-zone-demo',
template: `
<h2>Demo: NgZone</h2>
<p>Progress: {{progress}}%</p>
<p *ngIf="progress >= 100">Done processing {{label}} of Angular zone!</p>
<button (click)="processWithinAngularZone()">Process within Angular zone</button>
<button (click)="processOutsideOfAngularZone()">Process outside of Angular zone</button>
`,
})
export class NgZoneDemo {
progress: number = 0;
label: string;
constructor(private ngZone: NgZone) {}
// Loop inside the Angular zone ,so the UI DOES refresh after each setTimeout cycle
// 在angular Zone区域中循环,所以 UI 在每个 setTimeout 周期后都会刷新
// 所以此示例中,点击按钮后,页面在 progress=20,40,60,80,100时刷新了,刷新了5次
processWithinAngularZone() {
this.label = 'inside';
this.progress = 0;
this._increaseProgress(() => console.log('Inside Done!'));
}
// Loop outside of the Angular zone,so the UI DOES NOT refresh after each setTimeout cycle
// 在 Angular Zone区域外循环,所以每次 setTimeout 循环后 UI 不会刷新。
// 所以此示例中,点击按钮后,页面会刷新两次,第一次是progress=20,第二次是progress=100
processOutsideOfAngularZone() {
this.label = 'outside';
this.progress = 0;
this.ngZone.runOutsideAngular(() => {
this._increaseProgress(() => {
// reenter the Angular zone and display done
// 重新进入Angular区域并显示完成
this.ngZone.run(() => {
console.log('Outside Done!');
});
});
});
}
_increaseProgress(doneCallback: () => void) {
this.progress += 20;
console.log(`Current progress: ${this.progress}%`);
if (this.progress < 100) {
window.setTimeout(() => {
console.log('setTimeout了');
this._increaseProgress(doneCallback);
}, 10);
} else {
doneCallback();
}
}
}
版权声明:本文为Kate_sicheng原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。