这篇很棒的文章如果你认为ngDoCheck
你的组件正在被检查——请阅读这篇文章,深入解释错误。
此答案的内容基于角度版本 2.xx 对于最新版本 4.xx,请参阅此帖子。
互联网上没有关于变更检测的内部工作原理,所以我不得不花大约一周的时间调试源代码,所以这个答案在细节上将是相当技术性的。
角度应用程序是视图树(AppView
由编译器生成的组件特定类扩展的类)。每个视图都有一个存在于cdMode
属性中的更改检测模式。的默认值为,cdMode
即。ChangeDetectorStatus.CheckAlways
cdMode = 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 将运行变化检测。cdMode
ChangeDetectorStatus.Checked
ChangeDetectorStatus.Errored
onPush
cdMode
ChangeDetectorStatus.CheckOnce
所以ngDoCheck
不会覆盖更改的检测,它只是在每个更改的检测周期中调用,唯一的工作是将当前视图设置cdMode
为checkOnce
,以便在下一个更改检测周期中检查更改。有关详细信息,请参阅此答案。如果当前视图的变化检测模式是checkAlways
(如果不使用onPush策略则默认设置),ngDoCheck
似乎没什么用。