Change detection и оптимизация

Change detection — это процесс, которым Angular узнаёт, что данные изменились, и решает, какой кусок DOM перерисовать.

«Скорость интерфейса — это не магия, а контроль над тем, как часто и где Angular ищет изменения».

Откуда Angular знает, что count изменился и пора обновить экран? За это отвечает обнаружение изменений (change detection). Классически Angular использовал библиотеку Zone.js, которая перехватывала все асинхронные события (клики, таймеры, ответы сети) и после каждого запускала проверку всего дерева компонентов. Это работает, но при больших деревьях расточительно.

Первый рычаг оптимизации — стратегия OnPush. Она говорит: проверяй этот компонент только если изменился его вход (по ссылке) или внутри сработало событие/сигнал.

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

@Component({
  selector: 'app-product-card',
  standalone: true,
  changeDetection: ChangeDetectionStrategy.OnPush,
  template: `<h2>{{ title() }}</h2>`,
})
export class ProductCardComponent {
  title = input.required<string>();
}

Главный же сдвиг современного Angular — zoneless-режим: отказ от Zone.js в пользу сигналов. Когда состояние хранится в сигналах, фреймворк точно знает, что и когда изменилось, — и обновляет только затронутые узлы, без тотальной проверки.

// main.ts — приложение без Zone.js
import { provideZonelessChangeDetection } from '@angular/core';

bootstrapApplication(AppComponent, {
  providers: [provideZonelessChangeDetection()],
});

Как работает под капотом

В классической модели любое асинхронное событие запускает обход дерева сверху вниз: каждый компонент проверяет свои привязки. С OnPush ветки, чьи входы не менялись, пропускаются. В zoneless-модели обхода «на всякий случай» нет вовсе: сигнал, который изменился, сам помечает зависимые узлы как грязные, и Angular обновляет точечно их.

   Классика (Zone.js):  событие -> проверить ВСЁ дерево
        Root -> A -> B -> C ...  (даже неизменное)

   Сигналы (zoneless):  count.set(5)
        |
   помечен только узел, читающий count -> обновить ЕГО
        (остальное не трогаем)

Частые ошибки

  • Мутировать объект-вход на месте при OnPush. Ссылка не меняется — компонент не обновится. Нужна новая ссылка.
  • Тяжёлые функции в шаблоне. Они вызываются на каждой проверке; кешируйте через computed.
  • Смешивать сигналы и привычки Zone.js бездумно при переходе на zoneless.

Best practices

  • Используйте OnPush по умолчанию и иммутабельные данные для входов.
  • Храните состояние в сигналах — это естественно ведёт к точечным обновлениям.
  • Избегайте вызовов методов в шаблоне ради значений; считайте их в computed.

Итоги. Change detection решает, где перерисовать DOM. OnPush сужает проверки, а сигнальный zoneless-режим убирает тотальный обход вовсе, делая приложение быстрым. Дальше — финальная сборка и публикация.

Закрепляем

Change detection — это процесс, которым Angular узнаёт об изменениях данных и решает, какие узлы DOM перерисовать. Классически за это отвечал Zone.js: он перехватывал асинхронные события и после каждого запускал проверку всего дерева компонентов сверху вниз. Это работает, но при больших деревьях расточительно. Первый рычаг оптимизации — стратегия OnPush, которая разрешает проверять компонент только при изменении входа по ссылке, событии или сигнале, пропуская неизменные ветки.

Главный сдвиг современного Angular — переход к сигналам и zoneless-режиму. Когда состояние хранится в сигналах, фреймворк точно знает, какие узлы от какого сигнала зависят, и при изменении помечает грязными только их. Тотальный обход «на всякий случай» становится не нужен — отсюда и отказ от Zone.js. Чтобы не мешать этой машинерии, соблюдайте иммутабельность (особенно при OnPush: мутация объекта-входа на месте не сменит ссылку и не вызовет обновления) и не вызывайте тяжёлые методы прямо в шаблоне — выносите вычисления в computed. Тогда приложение остаётся быстрым даже при сотнях компонентов.

РежимКак проверяет изменения
Default (Zone.js)Всё дерево после каждого события
OnPushТолько при смене входа/события/сигнала
Zoneless (сигналы)Точечно — затронутые узлы
Проверьте себя
1. Что делает стратегия ChangeDetectionStrategy.OnPush?
AОтключает обновления
BПроверяет компонент только при изменении входа по ссылке, события или сигнала — а не на каждое асинхронное событие
CУскоряет сеть
DПерерисовывает всё дерево чаще
2. Почему сигналы естественно ведут к zoneless-режиму?
AОни отключают интерфейс
BСигнал точно знает, какие узлы от него зависят, поэтому Angular обновляет их точечно без тотального обхода дерева
CОни работают только без интернета
DЭто случайное совпадение