Zone.js — важная часть Angular, которая обнаруживает потенциальные изменения в состоянии приложения. Он фиксирует асинхронные операции, такие как setTimeout, сетевые запросы и прослушиватели событий. Расписание Angular обнаруживает изменения на основе сигналов от Zone.js.

Однако бывают случаи, когда запланированные задачи или микрозадачи не изменяют модель данных, что делает ненужным запуск обнаружения изменений. Общие примеры включают такие операции, как requestAnimationFrame, setTimeout, setInterval или планирование задач сторонними библиотеками. В таких сценариях важно идентифицировать эти ненужные вызовы обнаружения изменений и запускать код вне зоны Angular для оптимизации производительности.

Выявление ненужных вызовов обнаружения изменений

Angular DevTools может помочь обнаружить ненужные вызовы обнаружения изменений. Они часто появляются в виде последовательных полос на временной шкале профилировщика, происходящих из setTimeout, setInterval, requestAnimationFrame или обработчиков событий. Когда в вашем приложении есть ограниченные вызовы этих API, чрезмерные вызовы обнаружения изменений обычно вызываются сторонними библиотеками.

Запуск задач за пределами NgZone

Чтобы решить эту проблему, Angular предоставляет службу NgZone, позволяющую указать Angular избегать вызова обнаружения изменений для задач, запланированных определенным кодом. Выполняя код за пределами зоны Angular, можно избежать ненужных вызовов обнаружения изменений.

import { Component, NgZone, OnInit } from '@angular/core';

@Component({...})
class AppComponent implements OnInit {
  constructor(private ngZone: NgZone) {}

  ngOnInit() {
    this.ngZone.runOutsideAngular(() => {
      setInterval(pollForUpdates, 500);
    });
  }
}

Фрагмент кода выше демонстрирует, как использовать runOutsideAngular из службы NgZone. Вызывая setInterval внутри runOutsideAngular, Angular получает указание выполнить pollForUpdates за пределами зоны Angular и пропустить обнаружение изменений после этого.

Сторонние библиотеки часто запускают ненужные циклы обнаружения изменений, потому что они были созданы без учета Zone.js. Чтобы избежать этих дополнительных циклов, рекомендуется выполнять библиотечные API за пределами зоны Angular.

import { Component, NgZone, OnInit } from '@angular/core';
import * as Plotly from 'plotly.js-dist-min';

@Component({...})
class AppComponent implements OnInit {
  constructor(private ngZone: NgZone) {}

  ngOnInit() {
    this.ngZone.runOutsideAngular(() => {
      Plotly.newPlot('chart', data);
    });
  }
}

В приведенном выше примере выполнение Plotly.newPlot('chart', data) внутри runOutsideAngular гарантирует, что Angular не запускает обнаружение изменений после задач, запланированных логикой инициализации. Например, если Plotly.newPlot добавляет прослушиватели событий к элементу DOM, Angular не будет выполнять обнаружение изменений после выполнения их обработчиков.

Используя метод runOutsideAngular от NgZone, вы можете эффективно устранять загрязнение зоны и оптимизировать производительность вашего приложения Angular. Это позволяет избежать ненужных вызовов обнаружения изменений, вызванных внешними библиотеками, и гарантировать, что обнаружение изменений запускается только при необходимости.