这篇伟大的文章If you think ngDoCheck
means your component is being checked — read this article深入地解释了这个错误.
此答案的内容基于ANGLE版本2.x.x.有关最新版本4.x.x的信息,请参见this post.
互联网上没有任何关于变化检测内部工作原理的东西,所以我不得不花大约一周的时间调试源代码,所以这个答案在细节上将是相当技术性的.
angular应用程序是一个views树(AppView
类,由编译器生成的特定于组件的类扩展).每个视图都有一个位于cdMode
属性中的更改检测模式.cdMode
的默认值为ChangeDetectorStatus.CheckAlways
,即cdMode = 2
.
当变更检测循环运行时,每个父视图判断是否应该在子视图here上执行变更检测:
detectChanges(throwOnChange: boolean): void {
const s = _scope_check(this.clazz);
if (this.cdMode === ChangeDetectorStatus.Checked ||
this.cdMode === ChangeDetectorStatus.Errored)
return;
if (this.cdMode === ChangeDetectorStatus.Destroyed) {
this.throwDestroyedError('detectChanges');
}
this.detectChangesInternal(throwOnChange); <---- performs CD on child view
其中this
点指向child
视图.因此,如果cdMode
是ChangeDetectorStatus.Checked=1
,那么由于这一行,将跳过直接子代及其所有子代的更改检测.
if (this.cdMode === ChangeDetectorStatus.Checked ||
this.cdMode === ChangeDetectorStatus.Errored)
return;
changeDetection: ChangeDetectionStrategy.OnPush
所做的只是将cdMode
设置为ChangeDetectorStatus.CheckOnce = 0
,因此在第一次运行更改检测之后,子视图将其cdMode
设置为ChangeDetectorStatus.Checked = 1
,原因是this code:
if (this.cdMode === ChangeDetectorStatus.CheckOnce)
this.cdMode = ChangeDetectorStatus.Checked;
这意味着下一次更改检测周期开始时,将不会对子视图执行任何更改检测.
如何为此类视图运行更改检测的选项很少.第一个是将子视图的cdMode
更改为ChangeDetectorStatus.CheckOnce
,这可以使用ngDoCheck
生命周期中的this._changeRef.markForCheck()
钩子来完成:
constructor(private _changeRef: ChangeDetectorRef) { }
ngDoCheck() {
this._changeRef.markForCheck();
}
这只是将当前视图及其父视图的cdMode
更改为ChangeDetectorStatus.CheckOnce
,因此下次执行更改检测时将判断当前视图.
请查看完整的示例here in the sources,以下是其要点:
constructor(ref: ChangeDetectorRef) {
setInterval(() => {
this.numberOfTicks ++
// the following is required, otherwise the view will not be updated
this.ref.markForCheck();
^^^^^^^^^^^^^^^^^^^^^^^^
}, 1000);
}
第二个选项是在视图本身上调用detectChanges
,如果cdMode
不是ChangeDetectorStatus.Checked
或ChangeDetectorStatus.Errored
,则在当前视图上调用run change detection.由于onPush
个Angular 设置为cdMode
到ChangeDetectorStatus.CheckOnce
,angular将运行更改检测.
所以ngDoCheck
不会覆盖更改的检测,它只是在每个更改的检测周期中调用,它唯一的任务是将当前视图cdMode
设置为checkOnce
,以便在下一个更改检测周期中判断更改.详情见this answer.如果当前视图的更改检测模式为checkAlways
(如果未使用onPush策略,则默认设置),ngDoCheck
似乎没有用处.