这篇很棒的文章如果你认为ngDoCheck你的组件正在被检查——请阅读这篇文章,深入解释错误。
此答案的内容基于角度版本 2.xx 对于最新版本 4.xx,请参阅此帖子。
互联网上没有关于变更检测的内部工作原理,所以我不得不花大约一周的时间调试源代码,所以这个答案在细节上将是相当技术性的。
角度应用程序是视图树(AppView由编译器生成的组件特定类扩展的类)。每个视图都有一个存在于cdMode属性中的更改检测模式。的默认值为,cdMode即。ChangeDetectorStatus.CheckAlwayscdMode = 2
当变更检测周期运行时,每个父视图都会在此处检查是否应该对子视图执行变更检测:
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因为此代码:
if (this.cdMode === ChangeDetectorStatus.CheckOnce)
this.cdMode = ChangeDetectorStatus.Checked;
这意味着下一次更改检测周期开始时,不会对子视图执行更改检测。
如何为此类视图运行更改检测的选项很少。首先是将子视图更改cdMode为ChangeDetectorStatus.CheckOnce,这可以this._changeRef.markForCheck()在ngDoCheck生命周期挂钩中使用:
constructor(private _changeRef: ChangeDetectorRef) { }
ngDoCheck() {
this._changeRef.markForCheck();
}
这只是cdMode将当前视图及其父视图更改为ChangeDetectorStatus.CheckOnce,因此下次执行更改检测时会检查当前视图。
在源代码中查看完整示例,但这里是它的要点:
constructor(ref: ChangeDetectorRef) {
setInterval(() => {
this.numberOfTicks ++
// the following is required, otherwise the view will not be updated
this.ref.markForCheck();
^^^^^^^^^^^^^^^^^^^^^^^^
}, 1000);
}
第二个选项是调用视图本身,如果is not或detectChanges,它将在当前视图上运行更改检测。由于angular 设置为,因此 angular 将运行变化检测。cdModeChangeDetectorStatus.CheckedChangeDetectorStatus.ErroredonPushcdModeChangeDetectorStatus.CheckOnce
所以ngDoCheck不会覆盖更改的检测,它只是在每个更改的检测周期中调用,唯一的工作是将当前视图设置cdMode为checkOnce,以便在下一个更改检测周期中检查更改。有关详细信息,请参阅此答案。如果当前视图的变化检测模式是checkAlways(如果不使用onPush策略则默认设置),ngDoCheck似乎没什么用。