> Как работает механизм Change Detection в Angular и из чего он состоит? (Angular)

Уровень: senior · Роль: frontend · Категория: Технические вопросы

Компании: LeverX

Стек: Angular

> Пример ответа

Короткий ответ

Change Detection в Angular - это механизм, который синхронизирует состояние компонента (модель) с DOM (представлением). Он состоит из Zone.js, перехватывающего асинхронные события, и дерева Change Detector'ов, каждый из которых проверяет выражения в шаблоне компонента. По умолчанию используется стратегия Default, проверяющая всё дерево, но можно переключиться на OnPush для оптимизации.

Подробное объяснение

Механизм Change Detection в Angular основан на двух ключевых компонентах: Zone.js и ChangeDetectorRef. Zone.js monkey-patches все асинхронные операции (setTimeout, Promise, addEventListener, XHR и т.д.), чтобы Angular знал, когда произошло событие, потенциально меняющее состояние. После каждого такого события Angular запускает цикл Change Detection.

Цикл начинается с корневого компонента и рекурсивно обходит всё дерево компонентов. Для каждого компонента Angular сравнивает текущие значения выражений в шаблоне с предыдущими (с помощью ===). Если значение изменилось, Angular обновляет соответствующую часть DOM. Важно: Angular не делает глубокое сравнение объектов - он проверяет только ссылку.

Change Detection состоит из нескольких этапов:

  1. Обновление входных свойств (@Input) дочерних компонентов.
  2. Вызов хуков жизненного цикла ngDoCheck, ngAfterContentChecked, ngAfterViewChecked.
  3. Проверка выражений в шаблоне и обновление DOM при необходимости.

Стратегия OnPush меняет поведение: компонент проверяется только если изменилась ссылка на @Input, или если в компоненте или его потомке произошло событие, или если явно вызван markForCheck(). Это существенно повышает производительность в больших приложениях.

Zone.js можно отключить (например, с NgZone.runOutsideAngular), и тогда Angular не будет автоматически запускать Change Detection - его нужно вызывать вручную через ChangeDetectorRef.detectChanges().

На практике

На практике важно понимать trade-off между удобством и производительностью. По умолчанию Angular проверяет всё дерево, что может быть дорого при сотнях компонентов. Для критичных к производительности мест стоит использовать OnPush и иммутабельные данные.

Частая проблема - мутация объектов вместо замены ссылки. Например, this.items.push(newItem) не вызовет Change Detection при OnPush, так как ссылка на массив не изменилась. Правильно: this.items = [...this.items, newItem].

Также стоит избегать дорогих вычислений в шаблонах - они выполняются при каждом цикле. Для сложных вычислений используйте чистые пайпы (@Pipe({ pure: true })) или мемоизацию.

Пример кода

TYPESCRIPT
// Компонент с OnPush
@Component({
selector: 'app-user',
template: `{{ user.name }}`,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class UserComponent {
@Input() user: User;
// Мутация не сработает
updateName(name: string) {
this.user.name = name; // Не обновит DOM
}
// Правильный подход - новая ссылка
updateUser(name: string) {
this.user = { ...this.user, name };
}
}
TYPESCRIPT
// Явный запуск Change Detection
constructor(private cdr: ChangeDetectorRef) {}
someAsyncOperation() {
this.zone.runOutsideAngular(() => {
someExternalLibrary.onUpdate(() => {
this.cdr.detectChanges(); // Ручной запуск
});
});
}

Как отвечать на собеседовании

Начни с краткого определения, затем опиши архитектуру: Zone.js как источник событий и дерево Change Detector'ов. Обязательно упомяни стратегии Default и OnPush, объясни разницу. Приведи пример, когда OnPush не срабатывает из-за мутации, и как это исправить. Если спросят про производительность, расскажи про detectChanges() и markForCheck(). Не углубляйся в детали реализации Zone.js, если не спрашивают.

Что проверяет интервьюер

Интервьюер оценивает понимание того, как Angular узнаёт об изменениях (Zone.js), как происходит проверка (дерево компонентов), и как этим управлять (стратегии, ChangeDetectorRef). Важно показать, что ты понимаешь не только "как", но и "почему" - например, почему OnPush требует иммутабельности. Также проверяется способность объяснить сложную тему простыми словами.

Типичные ошибки

  • Путать Change Detection с двумясторонним связыванием (ngModel) - это разные вещи.
  • Думать, что OnPush проверяет только сам компонент - на самом деле он проверяет и потомков, если они не OnPush.
  • Считать, что Zone.js перехватывает все события - например, WebSocket или IndexedDB не патчатся по умолчанию.
  • Использовать detectChanges() там, где нужен markForCheck() - первый запускает проверку немедленно, второй помечает компонент для проверки в следующем цикле.

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью