Владислав Калачев

Как работает Change Detection в Angular (и почему это важно)?

7

В реактивном фреймворке Angular изменение модели должно сразу отражаться в интерфейсе. За это отвечает механизм Change Detection. Понимая его устройство, вы избегаете багов, ускоряете приложение и пишете ясный код. В этой статье разберём, как Angular отслеживает изменения, какие есть стратегии, и как их применять на практике.

Зачем нужен Change Detection

Производительность. Автоматическое обновление может стать узким местом.
Целостность данных. Без надёжного механизма модель и представление расходятся.
Простота отладки. Зная внутренние процессы, вы быстро находите и устраняете ошибки.

Базовый принцип

Angular встраивает свой механизм поверх JavaScript. Он:

1. «Патчит» асинхронные события через NgZone.
2. Запускает проверку на каждом таком событии.
3. Сравнивает старые и новые значения привязок (binding).
4. Обновляет DOM, только если значение изменилось.

NgZone и зоны выполнения

Angular использует библиотеку zone.js. Зона — это контекст, который отслеживает:

setTimeout, setInterval
— Промисы (Promise)
— События DOM
— HTTP-запросы

Каждое завершённое асинхронное действие автоматически запускает цикл Change Detection.

Дерево детекторов (ChangeDetector Tree)

— Каждый компонент получает свой ChangeDetector.
— Детекторы организованы в древовидную структуру: корень — ApplicationRef, ветви — компоненты и их дети.
— При проверке Angular рекурсивно обходит дерево слева направо.

Стратегии проверки

Default

— Проверяется весь компонентный дерево.
— Происходит на каждом асинхронном событии.

Плюсы: ничего настраивать не нужно.

Минусы: большие приложения «тормозят».

OnPush

— Проверка только при:
1. Изменении @Input()
2. Внутреннем событии компонента (клик и т. д.)
— Требует иммутабельности: данные должны замещаться новыми объектами, а не мутироваться.

Пример:

@Component({
  selector: 'app-item',
  changeDetection: ChangeDetectionStrategy.OnPush,
  template: `{{ item.name }}`
})
export class ItemComponent {
  @Input() item!: { name: string };
}

Ручное управление

Иногда нужно выйти за рамки зон или стратегий:

detectChanges() — немедленно проверяет текущий компонент и его детей.
markForCheck() — помечает компонент для проверки в следующем цикле.
detach()/reattach() — отключает/включает проверку компонента целиком.

constructor(private cd: ChangeDetectorRef) {}

// принудительно
this.cd.detectChanges();

// отложенно
this.cd.markForCheck();

// полный контроль
this.cd.detach();
// ... какие-то действия
this.cd.reattach();

Дополнительные оптимизации

Пайпы

— Используйте pure: true для жёстко детерминированных вычислений.

— Angular вызывает чистые пайпы только при изменении входных данных.

trackBy

— При *ngFor всегда указывайте trackBy, чтобы минимизировать перестройку DOM.

{{ user.name }}
trackById(index: number, user: { id: number }) {
  return user.id;
}

Zone-less приложения

— При экстремальной оптимизации можно выключить NgZone и вручную контролировать Change Detection.

Immutability

— Избегайте глубоких мутаций.
— Любое обновление структуры данных создаёт новый объект.

Избегайте анонимных функций

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

Best Practices

По умолчанию ставьте OnPush. Так вы сразу соблюдаете иммутабельность и избегаете «невидимых» перерисовок.

Разбивайте логику на мелкие компоненты. Чем меньше компонент и меньше привязок — тем быстрее обновление.

Следите за подписками. Отписывайтесь в ngOnDestroy, чтобы не держать лишние зоны живыми.

Используйте инструменты профилирования (Chrome DevTools, Angular DevTools), чтобы найти «тяжёлые» компоненты.

Заключение

Change Detection — ключ к отзывчивому приложению на Angular.

— Стратегия Default подходит для небольших проектов.
OnPush — выбор профессионала для масштабных приложений.
Ручное управление, производительные пайпы и trackBy помогут выжать максимум производительности.

Понимайте, как работает механизм, и тогда ваш код будет быстрым, надёжным и простым в поддержке.

Комментарии:

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *