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

f

Вы когда-нибудь смотрели на код Angular и думали: «Почему ngOnInit вызывается первым? А где ngOnChanges? И куда пропало значение переменной?» Знакомо? Это как собирать мебель без инструкции — вроде все детали есть, а собрать ровно не получается. Жизненные циклы Angular — это та самая инструкция, которая объясняет, что и когда происходит внутри вашего компонента. И сегодня вы разберетесь в этом раз и навсегда.

Что такое жизненный цикл компонента и почему это не скучная теория

Жизненный цикл — это последовательность событий, которые Angular запускает автоматически: от создания компонента до его полного удаления из DOM. Каждое событие — это хук (hook), то есть специальный метод, который вы можете переопределить. Если вы знаете порядок этих хуков, вы сможете предсказать, когда загружать данные, когда подписываться на Observable, а когда — отписываться, чтобы не было утечек памяти.

Представьте, что вы строите дом. Сначала заливаете фундамент (constructor), потом возводите стены (ngOnInit), проводите электрику (ngAfterViewInit) и, наконец, сносите постройку (ngOnDestroy). Если вы начнете прокладывать провода до того, как стены готовы, получится хаос. Вот почему порядок хуков критичен.

Полный список хуков: от constructor до ngOnDestroy

В Angular есть 8 основных хуков жизненного цикла. Но не пугайтесь — вам не нужно использовать их все. Большинство реальных приложений обходятся тремя-четырьмя. Главное — знать, когда какой вызывается. Вот порядок вызова для компонента:

Как не запутаться в последовательности: реальный пример

Допустим, у вас есть компонент UserProfileComponent с входным свойством userId. Когда приходит новое значение userId, Angular сначала вызывает ngOnChanges. Вы видите: «Ага, userId изменился с 5 на 10». Потом вызывается ngOnInit — один раз. Затем ngDoCheck, ngAfterContentInit и так далее. Если вы попытаетесь загрузить данные в constructor, вы не узнаете, какой userId передан — он еще не установлен. Ошибка новичка №1.

Правильный подход: в ngOnInit делаете запрос к API, используя значение userId, которое уже доступно. А если userId может меняться в процессе жизни компонента, вы отслеживаете изменения в ngOnChanges и делаете новый запрос или используете switchMap из RxJS в ngOnChanges.

Тонкие моменты: когда стандартные хуки не срабатывают

Angular не вызывает повторно ngOnChanges, если свойство Input — это объект или массив, а вы изменяете его внутреннее состояние (например, добавляете элемент в массив). Angular смотрит на ссылку, а не на содержимое. Для таких случаев используйте ngDoCheck или immutable-подход (создавайте новый объект). Пример: если у вас есть список задач и вы добавляете задачу через push, ngOnChanges не сработает. Решение: сделать новый массив через spread-оператор — this.tasks = [...this.tasks, newTask].

Еще один нюанс: ngAfterViewInit не сработает, если у компонента нет шаблона (то есть он пустой). Это редкий случай, но бывает. И если вы используете директивы, которые модифицируют DOM, учтите: их жизненный цикл может отличаться от компонентного.

Практические советы: какие хуки использовать в реальных проектах

В 90% случаев вам понадобятся только:

Остальные хуки — это инструменты для специфических задач. ngDoCheck используйте только если уверены, что без него не обойтись (например, при работе с ChangeDetectionStrategy.OnPush и внешними библиотеками). ngAfterViewInit — для работы с ViewChild после рендера. И никогда не кладите тяжелую логику в constructor — это замедлит рендеринг.

Чем этот подход отличается от других фреймворков

В React нет хуков жизненного цикла компонента в классическом смысле — есть useEffect, который работает по-другому. В Vue есть watch и computed, но порядок вызова проще. Angular же строго регламентирует последовательность и дает тонкий контроль. Это как разница между автоматом и ручной коробкой передач: Angular требует больше внимания на старте, но дает больше возможностей для оптимизации.

Например, если вам нужно выполнить код после того, как все дочерние компоненты отрендерились, в Angular вы просто используете ngAfterViewInit. В React пришлось бы добавлять useLayoutEffect или setTimeout. Разница в пять строк кода, которая может спасти от багов.

Типичные ошибки и как их избежать

Следуя этим правилам, вы сэкономите часы дебаггинга и сделаете приложение стабильнее.

Добавлено: 23.04.2026