Оглавление
В реактивном фреймворке 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 помогут выжать максимум производительности.
Понимайте, как работает механизм, и тогда ваш код будет быстрым, надёжным и простым в поддержке.
Комментарии: