除了简单的通知之外,我似乎不明白为什么需要ngDoCheck生命周期钩子,特别是在它内部编写代码对于更改检测有什么不同.我发现的大多数示例都显示了一些无用的示例,比如this one个,具有大量日志(log)记录功能.

此外,在生成的类中,除了简单的通知之外,我没有看到它被用于其他用途:

100

Wrapper_AppComponent.prototype.ngDoCheck = function(view,el,throwOnChange) {
  var self = this;
  var changed = self._changed;
  self._changed = false;
  if (!throwOnChange) {
    if (changed) {
      jit_setBindingDebugInfoForChanges1(view.renderer,el,self._changes);
      self._changes = {};
    }
    self.context.ngDoCheck(); <----------- this calls ngDoCheck on the component
                                               but the result is not used 
                                               anywhere and no params are passed
  }
  return changed;
};

推荐答案

这篇伟大的文章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视图.因此,如果cdModeChangeDetectorStatus.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.CheckedChangeDetectorStatus.Errored,则在当前视图上调用run change detection.由于onPush个Angular 设置为cdModeChangeDetectorStatus.CheckOnce,angular将运行更改检测.

所以ngDoCheck不会覆盖更改的检测,它只是在每个更改的检测周期中调用,它唯一的任务是将当前视图cdMode设置为checkOnce,以便在下一个更改检测周期中判断更改.详情见this answer.如果当前视图的更改检测模式为checkAlways(如果未使用onPush策略,则默认设置),ngDoCheck似乎没有用处.

Angular相关问答推荐

如何从Angular TS文件传递$Events对象?

以Angular 表示显示特殊字符

Angular 17@在大小写时切换多个值

如何使用独立组件在ANGLE(16+)中实现嵌套布线

Angular 应用程序未在Azure应用程序服务中运行

一次重定向后,所有子路由都处于活动状态

根据环境变量动态加载Angular templateUrl

SignalR:协商有效,但消息未发送(.NET/Angular)

Angular APP_INITIALIZER 中的 MSAL 身份验证

如何使用指令以Angular 传递默认值

Angular SPA 和 .Net 6 API 之间未连接 Application Insights Map

如何从父路由的组件访问激活的子路由的数据?

如何在angular material中使用输入类型文件

Angular 5 Mat-grid 列表响应式

Angular Material 模态对话框周围的空白

Angular 2 - ngfor 没有包装在容器中

从 angular2 模板调用静态函数

交替行 colored颜色 angular material表

提交后在Angular 2中重置表单

如何在 Angular 2 中链接 HTTP 调用?