Внести вклад
Лучшие практики
Производительность

Пропуск поддеревьев компонентов

JavaScript по умолчанию использует изменяемые структуры данных, на которые могут ссылаться разные компоненты. Angular запускает обнаружение изменений по всему дереву компонентов, чтобы актуальное состояние структур данных отражалось в DOM.

Для большинства приложений обнаружение изменений достаточно быстрое. Но при особенно большом дереве компонентов проход по всему приложению может вызывать проблемы с производительностью. Это можно решить, настроив обнаружение изменений так, чтобы оно выполнялось только на части дерева.

Использование OnPush

OnPush — стратегия обнаружения изменений по умолчанию в Angular (начиная с v22). Она указывает Angular запускать обнаружение изменений для поддерева компонентов только когда:

  • Корневой компонент поддерева получает новые input в результате привязки в шаблоне. Angular сравнивает текущее и прошлое значение input через ==.
  • Angular обрабатывает событие (например, через event binding, output binding или @HostListener) в корневом компоненте поддерева или в любом из его потомков — независимо от того, используют ли они OnPush.

Типовые сценарии обнаружения изменений

В этом разделе рассматриваются несколько типовых сценариев, иллюстрирующих поведение Angular.

Событие обрабатывается компонентом с Eager обнаружением изменений

Если Angular обрабатывает событие внутри компонента со стратегией Eager, фреймворк выполняет обнаружение изменений по всему дереву компонентов. Angular пропустит поддеревья-потомки с корнями на OnPush, которые не получили новых input.

Например, если стратегия обнаружения изменений MainComponentOnPush, а пользователь взаимодействует с компонентом вне поддерева с корнем MainComponent, Angular проверит все розовые компоненты на диаграмме ниже (AppComponent, HeaderComponent, SearchComponent, ButtonComponent), если только MainComponent не получит новые input:

AppComponent

HeaderComponent

MainComponent (OnPush)

SearchComponent

ButtonComponent

LoginComponent (OnPush)

DetailsComponent

Event

Событие обрабатывается компонентом с OnPush

Если Angular обрабатывает событие внутри компонента со стратегией OnPush, фреймворк выполнит обнаружение изменений по всему дереву. Angular проигнорирует поддеревья с корнями на OnPush, которые не получили новых input и находятся вне компонента, обработавшего событие.

Например, если Angular обрабатывает событие внутри MainComponent, фреймворк запустит обнаружение изменений по всему дереву. Angular проигнорирует поддерево с корнем LoginComponent, потому что у него OnPush, а событие произошло вне его области.

AppComponent

HeaderComponent

MainComponent (OnPush)

SearchComponent

ButtonComponent

LoginComponent (OnPush)

DetailsComponent

Event

Событие обрабатывается потомком компонента с OnPush

Если Angular обрабатывает событие в компоненте с OnPush, фреймворк выполнит обнаружение изменений по всему дереву, включая предков компонента.

Например, на диаграмме ниже Angular обрабатывает событие в LoginComponent с OnPush. Angular вызовет обнаружение изменений во всём поддереве, включая MainComponent (родитель LoginComponent), хотя у MainComponent тоже OnPush. Angular проверяет и MainComponent, потому что LoginComponent — часть его view.

AppComponent

HeaderComponent

MainComponent (OnPush)

SearchComponent

ButtonComponent

LoginComponent (OnPush)

DetailsComponent

Event

Новые input у компонента с OnPush

Angular запустит обнаружение изменений во дочернем компоненте с OnPush при установке input-свойства в результате привязки в шаблоне.

Например, на диаграмме ниже AppComponent передаёт новый input в MainComponent с OnPush. Angular запустит обнаружение изменений в MainComponent, но не в LoginComponent, у которого тоже OnPush, если только тот тоже не получит новые input.

AppComponent

HeaderComponent

MainComponent (OnPush)

SearchComponent

ButtonComponent

LoginComponent (OnPush)

DetailsComponent

Parent passes new input to MainComponent

Краевые случаи

  • Изменение input-свойств в коде TypeScript. Если через API вроде @ViewChild или @ContentChild получить ссылку на компонент в TypeScript и вручную изменить свойство @Input, Angular не запустит обнаружение изменений для OnPush-компонентов автоматически. Если нужно запустить обнаружение изменений, внедрите ChangeDetectorRef и вызовите changeDetectorRef.markForCheck(), чтобы Angular запланировал обнаружение изменений.
  • Изменение ссылок на объекты. Если input получает изменяемый объект и вы меняете объект, сохраняя ссылку, Angular не вызовет обнаружение изменений. Это ожидаемое поведение: предыдущее и текущее значение input указывают на одну и ту же ссылку.