Как угловое разрешает бесконечную рекурсию? - PullRequest
0 голосов
/ 04 октября 2018

Я следую этому курсу Удеми: https://www.udemy.com/the-complete-guide-to-angular-2/

Я занимаюсь частью 2.13 по интерполяции строк.

В рамках урока вы определяете простую функцию в своем компоненте дляотобразите одно из свойств вашего класса:

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

    getServerStatus(){
        return this.serverStatus;
    }
}

Затем в своем компоненте HTML вы привяжете эту функцию к шаблону:

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

То, что я хотел проверить, это то, что происходиткогда вы связываете этот тег строковой интерполяции с функцией, которая является самореференциальной ... Например:

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

Когда я запускаю этот код, я замечаю, что на странице отображается следующее:

server with ID 10 is offlineofflineofflineofflineofflineofflineofflineoffline

Ровно с 8 повторениями переменной serverStatus.

Что меня интересует, почему это количество равно 8 повторениям?Какую логику использует angular, чтобы решить, что шаблонная директива «в реальном времени» отключается от свойства класса после 8 повторений.

1 Ответ

0 голосов
/ 04 октября 2018

Угловые запуски два цикла обнаружения изменений в начале приложения.

Другими словами, он вызывает Application.tick() метод два раза

1) После начальной загрузки mainкомпонент (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) И при первом повороте виртуальной машины (когда в 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) Угловые прогоны изменяют цикл обнаружения дважды .

Еще один момент здесьзаключается в том, что метод tick выполняется в блоке try catch.

Итак, что мы имеем до сих пор?

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

Также на каждом view.detectChanges() Angular проверяет, имеют ли привязки шаблонаизменено или нет. Для этого Angular выполняет каждое выражение в шаблоне (в результате ваш метод getServerStatus() будетвыполняется при каждом обходе дерева).Если во время второго метода cd withih tick произошли некоторые изменения в привязке, то Angular выдает ошибку Expression has changed after it was checked.Как вы можете догадаться, это не остановит последующие циклы CD, спасибо try catch block.

Ради простоты, скажем, у вас есть следующий шаблон:

{{ 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!!)

В результате вы получите ровно 8 повторений serverStatus

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...