Angular

Жизненный цикл и afterRender

Команда переносит виджет графиков на Angular и в ngOnInit обращается к canvas, чтобы инициализировать библиотеку рисования. На сервере при SSR код падает: document не существует, а сам элемент ещё не отрисован. Так выясняется главное правило про жизненный цикл - момент, когда компонент создан, и момент, когда его DOM реально нарисован браузером, это разные точки во времени. Angular 21 дал для этого отдельный инструмент: afterRender и afterNextRender, которые срабатывают именно тогда, когда DOM готов, и только в браузере.

  • Графические библиотеки (Chart.js, D3, three.js): инициализация требует реального DOM-элемента, а не момента создания компонента
  • SSR и Angular Universal: код в ngOnInit выполняется на сервере, где нет document и window, поэтому работа с DOM выносится в afterNextRender
  • Очистка ресурсов: подписки, таймеры и WebSocket-соединения освобождаются в ngOnDestroy, иначе утечки памяти при навигации между страницами
  • Измерение размеров (getBoundingClientRect), фокус на поле ввода, интеграция с не-Angular кодом - всё это работа после отрисовки
  • Миграция на сигналы: проекты постепенно убирают ngOnChanges в пользу input() и computed, упрощая компоненты

Предварительные знания

  • Понимание, что такое компонент Angular и как он создаётся
  • Базовое знание привязки данных и входных свойств
  • Идея о серверном рендеринге (SSR): часть кода выполняется без браузера

Зачем понадобились afterRender и afterNextRender

До Angular 16 у разработчиков не было официального хука, который гарантировал бы, что весь DOM приложения отрисован. Хук ngAfterViewInit срабатывал после инициализации представления одного компонента, но не давал гарантии про всю страницу, а при SSR выполнялся и на сервере. Команда Angular ввела afterRender и afterNextRender как часть движения к сигналам и zoneless. Эти колбэки выполняются только в браузере после того, как Angular закончил отрисовку, что сделало работу с DOM предсказуемой и безопасной для серверного рендеринга. К Angular 21, где zoneless стал стандартом, эта пара колбэков заняла место главного инструмента для манипуляций с реальным DOM.

Хуки жизненного цикла: от создания до уничтожения

Компонент Angular проходит фиксированную последовательность этапов: Angular создаёт его экземпляр, устанавливает входные свойства, отрисовывает шаблон, обновляет его при изменениях и в конце удаляет. На ключевых этапах фреймворк вызывает методы компонента, если они объявлены. Эти методы и называют хуками жизненного цикла. Каждый хук объявлен в отдельном интерфейсе (OnInit, OnChanges, OnDestroy и так далее), что помогает не ошибиться в имени метода.

ХукКогда вызываетсяТипичное применение
ngOnChangesПри установке и каждом изменении входных свойствРеакция на новое значение @Input, пересчёт зависимых данных
ngOnInitОдин раз после первого ngOnChangesНачальная загрузка данных, настройка компонента
ngDoCheckПри каждом цикле обнаружения измененийРучная проверка изменений, которые Angular не ловит сам
ngAfterViewInitПосле инициализации представления компонентаДоступ к дочерним элементам через ViewChild
ngOnDestroyПеред удалением компонентаОтписка, остановка таймеров, освобождение ресурсов

Самая частая утечка памяти в Angular - подписка без отписки. Если ресурс создан в ngOnInit, его освобождение в ngOnDestroy обязательно. При навигации между страницами старые компоненты удаляются, и забытая подписка продолжает работать в фоне.

Чем ngOnInit отличается от ngOnChanges по частоте вызова?

Работа с DOM: afterRender и afterNextRender

Момент создания компонента и момент, когда браузер реально нарисовал его DOM, разнесены во времени. Хуки вроде ngOnInit срабатывают рано, когда элементов на странице ещё может не быть, а при серверном рендеринге выполняются в среде без document и window. Для работы с реальным DOM Angular даёт два колбэка: afterNextRender выполняется один раз после следующей отрисовки, afterRender - после каждой. Оба работают только в браузере, поэтому код внутри них безопасен для SSR.

  • afterNextRender — Срабатывает один раз после ближайшей отрисовки. Подходит для одноразовой инициализации: создание графика, установка фокуса, замер размеров
  • afterRender — Срабатывает после каждой отрисовки. Используется реже и осторожно, например для постоянной синхронизации с внешней DOM-библиотекой. Тяжёлая работа здесь бьёт по производительности

afterRender и afterNextRender вызываются в контексте инъекции, поэтому их регистрируют в конструкторе или в фабрике провайдера. Внутри доступен inject, а сами колбэки автоматически прекращают работу при удалении компонента.

Почему инициализацию графической библиотеки, которой нужен canvas, лучше делать в afterNextRender, а не в ngOnInit?

Как сигналы сократили опору на хуки

Раньше реакция на изменение входного свойства писалась в ngOnChanges: сравнить старое и новое значение, пересчитать зависимое поле. В Angular 21 входное свойство объявляют через input(), и оно становится сигналом. Производные значения выражают через computed, а побочные эффекты - через effect. В результате логика, которая занимала несколько хуков, превращается в пару декларативных строк, а компонент перестаёт зависеть от ручного жизненного цикла.

  • ngOnChanges для пересчёта производных значений заменяется на computed
  • Реактивные побочные эффекты переезжают из ngOnInit и ngDoCheck в effect
  • Доступ к дочерним элементам идёт через сигнальный viewChild вместо ngAfterViewInit и декоратора ViewChild
  • Отписку от RxJS-потоков берёт на себя takeUntilDestroyed вместо ручного ngOnDestroy

Хуки никуда не исчезли и остаются валидными. ngOnDestroy по-прежнему нужен для ресурсов вне реактивной системы, а afterNextRender - для прямой работы с DOM. Сигналы убирают именно рутину вокруг производного состояния и реактивных эффектов.

Какой хук жизненного цикла чаще всего становится лишним при переходе на computed для производных значений?

Связь с другими темами

Жизненный цикл - мост между классическим Angular и сигнальной моделью. Дальше курс раскрывает реактивную часть:

  • Сигналы — signal и computed снимают часть работы с ngOnChanges и ngDoCheck, делая компонент короче
  • effect — effect берёт на себя реактивные побочные эффекты, которые раньше писали в хуках вручную
  • Сигнальные запросы — viewChild возвращает сигнал, а afterNextRender даёт момент, когда к этому DOM-элементу можно безопасно обратиться

Итог

  • Жизненный цикл компонента - это последовательность моментов от создания до уничтожения, для каждого есть свой хук
  • ngOnInit вызывается один раз после установки входных свойств, ngOnChanges - при каждом изменении входов, ngOnDestroy - перед удалением компонента
  • Создание компонента и отрисовка его DOM - разные моменты, поэтому прямая работа с DOM в ngOnInit ненадёжна
  • afterRender и afterNextRender выполняются только в браузере после отрисовки, что делает их безопасными для SSR и подходящими для интеграции с DOM-библиотеками
  • Сигналы (signal, computed, effect) в Angular 21 переносят реактивную логику из хуков в декларативные конструкции, сокращая опору на жизненный цикл

Связанные уроки

  • ng-11-signals-intro — Сигналы убирают часть работы, которая раньше держалась на ngOnChanges и ручных проверках. Логичный следующий шаг
  • ng-13-effect — effect перенимает роль реактивных побочных эффектов, которые раньше писали в ngOnInit и ngDoCheck
  • ng-15-signal-queries — afterNextRender часто используется вместе с сигнальными viewChild для доступа к DOM после первого рендера
Жизненный цикл и afterRender

0

1

Войти