它执行了 6 次,因为:
有一个tick
中的方法ApplicationRef
类,它是2 个变化检测周期执行 3 次。如果您将通过调用启用生产模式enableProdMode()
将会被执行3次
ApplicationRef
是对页面上运行的 Angular 应用程序的引用。这tick
方法正在运行从根到叶的变化检测。
这是如何tick
方法看起来(https://github.com/angular/angular/blob/2.3.0/modules/%40angular/core/src/application_ref.ts#L493-L509):
tick(): void {
if (this._runningTick) {
throw new Error('ApplicationRef.tick is called recursively');
}
const scope = ApplicationRef_._tickScope();
try {
this._runningTick = true;
this._views.forEach((view) => view.ref.detectChanges()); // check
if (this._enforceNoNewChanges) {
this._views.forEach((view) => view.ref.checkNoChanges()); // check only for debug mode
}
} finally {
this._runningTick = false;
wtfLeave(scope);
}
}
For 调试模式 tick
starts 两个变化检测周期. So detectChangesInternal
在编译视图中将被调用两次。
并且作为你的sortedTodos
property 是一个 getter,因此每次都会作为函数执行。
在这里阅读更多相关信息(Angular 2 中的变化检测)
那么我们就知道我们的sortedTodos
getter 被调用两次tick
为什么是tick
方法执行了3次?
1) First tick
通过引导应用程序手动运行。
private _loadComponent(componentRef: ComponentRef<any>): void {
this.attachView(componentRef.hostView);
this.tick();
https://github.com/angular/angular/blob/2.3.0/modules/%40angular/core/src/application_ref.ts#L479
2)Angular2 在 zonejs 中运行,因此它是管理更改检测的主要部分。上文提到的ApplicationRef
已订阅zone.onMicrotaskEmpty
.
this._zone.onMicrotaskEmpty.subscribe(
{next: () => { this._zone.run(() => { this.tick(); }); }});
https://github.com/angular/angular/blob/2.3.0/modules/%40angular/core/src/application_ref.ts#L433
onMicrotaskEmpty事件是一个指示器,当zone 变得稳定
private checkStable() {
if (this._nesting == 0 && !this._hasPendingMicrotasks && !this._isStable) {
try {
this._nesting++;
this._onMicrotaskEmpty.emit(null); // notice this
} finally {
this._nesting--;
if (!this._hasPendingMicrotasks) {
try {
this.runOutsideAngular(() => this._onStable.emit(null));
} finally {
this._isStable = true;
}
}
}
}
}
https://github.com/angular/angular/blob/2.3.0/modules/%40angular/core/src/zone/ng_zone.ts#L195-L211
因此,在执行某些 zonejs 任务后,会发出此事件
3)你正在使用angular2-in-memory-web-api
包,当您尝试获取模拟数据时,它会执行以下操作:
createConnection(req: Request): Connection {
let res = this.handleRequest(req);
let response = new Observable<Response>((responseObserver: Observer<Response>) => {
if (isSuccess(res.status)) {
responseObserver.next(res);
responseObserver.complete();
} else {
responseObserver.error(res);
}
return () => { }; // unsubscribe function
});
response = response.delay(this.config.delay || 500); // notice this
return {
readyState: ReadyState.Done,
request: req,
response
};
}
https://github.com/angular/in-memory-web-api/blob/0.0.20/src/in-memory-backend.service.ts#L136-L155
它启动常规的 zonejs 任务周期,这使得 zoneunStable
最后在上面描述的任务执行之后onMicrotaskEmpty
再次发生事件
您可以在这里找到有关 zonejs 的更多详细信息
- http://blog.kwintenp.com/how-the-hell-do-zones-really-work/
- http://blog.thoughtram.io/angular/2016/02/01/zones-in-angular-2.html
Recap:
正如您所看到的,您的解决方案有点错误。您不应该在模板中使用 getter 或函数作为绑定。您可以在这里找到可能的解决方案
- 为什么在使用函数时总是执行 Angular 2 中的 *ngIf ?