角度如何解决无限递归?

时间:2018-10-04 02:47:44

标签: angular angular-directive angular-components

我正在学习Udemy的课程: https://www.udemy.com/the-complete-guide-to-angular-2/

我参与了字符串插值的第2.13部分。

作为本课的一部分,您将在组件中定义一个简单函数以显示类的属性之一:

SSL_CTX_set_cipher_list

然后在组件HTML中,将该函数绑定到模板:

export class ServerComponent{
    serverId = 10;
    serverStatus = 'offline';

    getServerStatus(){
        return this.serverStatus;
    }
}

我要测试的是将字符串插值标签绑定到自引用函数时发生的情况。例如:

<P>Server with ID {{ serverId }} is {{ getServerStatus() }}</P>

运行此代码时,我注意到页面显示以下内容:

getServerStatus(){
    this.serverStatus = this.serverStatus + this.serverStatus;
    return this.serverStatus;
}

准确重复8个serverStatus变量。

我想知道的是为什么这个数量恰好是8次重复?八次重复后,angular使用哪种逻辑来确定“实时”模板指令与class属性无关。

1 个答案:

答案 0 :(得分:3)

角度启动在应用程序开始时两个变更检测周期

换句话说,它两次调用Application.tick()方法

1)自举主要组件(https://github.com/angular/angular/blob/aaaa34021c2d56f798d20e5a1f31b23972055170/packages/core/src/application_ref.ts#L539-L541

之后
private _loadComponent(componentRef: ComponentRef<any>): void {
  this.attachView(componentRef.hostView);
  this.tick();

2)并且在第一个VM转弯时(当zonejs中没有微任务时)(https://github.com/angular/angular/blob/aaaa34021c2d56f798d20e5a1f31b23972055170/packages/core/src/application_ref.ts#L385-L386

this._zone.onMicrotaskEmpty.subscribe(
  {next: () => { this._zone.run(() => { this.tick(); }); }});

考虑到这一点,让我们回到我们的Application.tick()方法。它在视图树(组件视图或嵌入式视图)上运行更改检测。

tick(): void {
   ...
    try {
      ...
      this._views.forEach((view) => view.detectChanges());
      if (this._enforceNoNewChanges) {
        this._views.forEach((view) => view.checkNoChanges());
      }
    } catch (e) {
      ...
    } finally {
      ...
    }
}

我们在这里能注意到什么?

我们注意到在开发模式下(因为this._enforceNoNewChanges = isDevMode(); https://github.com/angular/angular/blob/aaaa34021c2d56f798d20e5a1f31b23972055170/packages/core/src/application_ref.ts#L383 Angular运行两次更改检测周期

这里的另一点是,tick方法是在try catch块内执行的。

那么,到目前为止我们有什么?

2 сd cycles * 2 view.detectChanges() on the tree = 4

还在每个view.detectChanges()上,Angular都会检查模板绑定是否已更改。为此,Angular 执行模板中的每个表达式(因此,您的getServerStatus()方法将在每次遍历树时执行)。如果在第二个cd withhh tick方法期间绑定发生了一些变化,则Angular会引发错误Expression has changed after it was checked。如您所料,它不会停止后续的cd循环,谢谢try catch块。

为简单起见,假设您具有以下模板:

{{ getServerStatus() }}

那这里发生了什么?

Start app                                                              serverStatus 

 loadComponent => tick
                    |
                    |__ view.detectChanges()
                                   ||
                                   \/
                           call getServerStatus()                     'offlineoffline'

                    |__ view.checkNoChanges()
                                  ||
                                  \/
                           call getServerStatus()               'offlineofflineofflineoffline'

               'offlineoffline' !== 'offlineofflineofflineoffline'
                                  ||
                                  \/
    ExpressionChangedAfterItHasBeenCheckedError (template is not updated!!)

 onMicrotaskEmpty => tick
                    |
                    |__ view.detectChanges()
                                   ||
                                   \/
                           call getServerStatus()                     'offline'.repeat(8)

                    |__ view.checkNoChanges()
                                  ||
                                  \/
                           call getServerStatus()                     'offline'.repeat(16)

               'offline'.repeat(8) !== 'offline'.repeat(16)
                                  ||
                                  \/
     ExpressionChangedAfterItHasBeenCheckedError (template is not updated!!)

结果,您得到了serverStatus的精确8次重复