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 (сигналы) | Точечно — затронутые узлы |