setTimeOut() 在 Angular Lifecycle Hooks 中重复:ngDoCheck、ngAfterContentChecked、ngAfterViewChecked

Joy*_*nda 4 settimeout angular

再现性

我有一个小的 TypeScript 代码片段,如下所示:

ngAfterViewChecked(){
    console.log("ngAfterViewChecked 1");
    setTimeout(() => {
      console.log("ngAfterViewChecked 2");
    }, 1000);
  }
Run Code Online (Sandbox Code Playgroud)

函数setTimeOut()应该在 1 秒后调用 lambda 函数,然后停止。然而,钩子 ngAfterViewChecked() 被连续调用(摘自 Chrome 开发者工具控制台):

00:36:50.827 home.component.ts:53 ngAfterViewChecked 1
00:36:51.842 home.component.ts:55 ngAfterViewChecked 2
00:36:51.843 home.component.ts:53 ngAfterViewChecked 1
00:36:52.843 home.component.ts:55 ngAfterViewChecked 2
00:36:52.844 home.component.ts:53 ngAfterViewChecked 1
00:36:53.845 home.component.ts:55 ngAfterViewChecked 2
00:36:53.846 home.component.ts:53 ngAfterViewChecked 1
00:36:54.848 home.component.ts:55 ngAfterViewChecked 2
...
Run Code Online (Sandbox Code Playgroud)

如果没有setTimeOut(),函数ngAfterViewChecked()被调用一次。也有发生此问题ngDoCheck()ngAfterContentChecked()

使用其他 Angular Lifecycle Hooks 进行测试

在, , , 中声明的同一个代码体,无论有没有 ,都会按预期调用一次。setTimeOut()constructor()ngOnInit()ngAfterContentInit()ngAfterViewInit()

环境

  • 该程序非常简单(就像 Angular 的“Hello World”)。没有第 3 方库。
  • Angular 5 和 6 都有这个问题(低版本尚未测试)。它们分别安装在不同的机器上。

    Angular CLI: 6.0.8
    Node: 8.11.3
    OS: win32 x64
    Angular: 6.0.9
    
    
    Angular CLI: 1.7.4
    Node: 8.11.1
    OS: win32 x64
    Angular: 5.2.10
    
    Run Code Online (Sandbox Code Playgroud)
  • 视窗 8.1 x64。

  • 谷歌浏览器 67(64 位)。

我猜

在组件的整个生命周期中,钩子constructor()ngOnInit()、 都会被调用一次,而不管其内容在整个生命周期中如何演变。所以这个问题在这些钩子中不会发生。ngAfterContentInit()ngAfterViewInit()

使用ngDoCheck(),ngAfterContentChecked()ngAfterViewChecked(),它们会在 Angular检测到变化时被调用。但正如 lambda 函数体中所示,只使用了 simple console.log()。我认为 Angular 可能会拦截setTimeOut()调用并盲目地认为可能发生了一些更改,因此它启动了更改检测过程,这导致了我们所看到的:无限链接的调用。

这是错误还是功能?

mat*_*tmo 8

功能 - Angular 的更改检测在异步操作完成后运行(如 setTimeout) - 所以 setTimeout 本身会导致ngAfterViewChecked运行,反之亦然。

如果您不希望发生这种情况,您可以在 Angular 的区域之外运行,即

   ngAfterViewChecked(){
    console.log("ngAfterViewChecked 1");

    this.ngZone.runOutsideAngular(() => {
      setTimeout(() => {
        console.log("ngAfterViewChecked 2");
      }, 1000);
    });
  }
Run Code Online (Sandbox Code Playgroud)

您可以在构造函数中注入 NgZone,即private ngZone: NgZone. 查看 Angular 和 zone.js 之间的关系以获取更多信息。