Как работает Change Detection в Angular?
Что такое Change Detection?
Change Detection — это механизм Angular, который следит за изменениями данных и обновляет шаблон (DOM), если нужно.
Когда в приложении что-то изменяется — Angular запускает процесс обнаружения изменений и сравнивает значения в компоненте с тем, что уже отрисовано, чтобы определить, нужно ли перерендерить DOM.
Как это работает?
- Angular запускает проверку с самого корневого компонента.
- Проходит по дереву компонентов вниз.
- Проверяет каждый биндинг (
{{ value }},[property],(event)) на изменения. - Если значение изменилось — обновляется DOM.
Что запускает Change Detection?
Angular использует библиотеку zone.js, чтобы автоматически "захватывать" события, вызывающие изменения:
| Источник изменения | Пример |
|---|---|
| События DOM | click, input, change, и т.д. |
| Таймеры | setTimeout, setInterval |
| HTTP-запросы | HttpClient |
| Promise и async/await | fetch().then(...), await |
| Внутренние изменения | this.value = ... |
Пример: автоматическое обновление DOM
@Component({
selector: 'app-example',
template: `<p>{{ counter }}</p> <button (click)="increment()">+</button>`
})
export class ExampleComponent {
counter = 0;
increment() {
this.counter++;
}
}
Angular автоматически отслеживает изменение counter после клика по кнопке и обновляет DOM.
Change Detection Strategies
Angular поддерживает две стратегии обнаружения изменений:
| Стратегия | Описание |
|---|---|
Default | Angular проверяет все компоненты при любом изменении |
OnPush | Angular проверяет компонент только при изменении Input-параметров или событиях |
Использование OnPush:
@Component({
selector: 'app-optimized',
changeDetection: ChangeDetectionStrategy.OnPush,
template: `<p>{{ user.name }}</p>`
})
export class OptimizedComponent {
@Input() user!: { name: string };
}
Это уменьшает количество проверок, повышая производительность, особенно в больших приложениях.
Как Angular сравнивает значения?
Angular использует сравнение по ссылке (shallow reference check):
this.user.name = "John"; // не триггерит OnPush!
this.user = { name: "John" }; // триггерит, потому что новая ссылка
Чтобы обновить компонент со стратегией OnPush — нужно создать новый объект.
Как оптимизировать Change Detection?
- Используйте
ChangeDetectionStrategy.OnPushвезде, где это возможно. - Избегайте частых мутаций объектов/массивов (используйте иммутабельность).
- Используйте
trackByв*ngFor, чтобы избежать лишних перерендеров. - Работайте с
ChangeDetectorRefиNgZoneдля ручного управления:
constructor(private cdr: ChangeDetectorRef) {}
update() {
this.value = newValue;
this.cdr.detectChanges(); // вручную инициируем проверку
}
Важно:
OnPush не означает, что компонент никогда не будет обновляться. Он всё ещё реагирует на изменения входных данных и события (click, input).
Важно:
Использование ChangeDetectorRef.detectChanges() считается bad practice, так как это ломает автоматический механизм обнаружения изменений, дергает лишний рендеринг, который может быть не нужен и создает непредсказуемое поведение. Для оптимизации обычно используют markForCheck() или detatch/reattach(последних ни разу на живом проекте не видел)
У Ангуляра есть механизмы реактивного програмированния сигналы, обзерваблы и инпуты. Стоит лучше использовать их для корректного рендеринга.
Подробнее прочитать можно здесь.