Какой цикл жизни у компонентов?

В Angular каждый компонент проходит через определённый жизненный цикл, начиная с его создания и заканчивая уничтожением. Angular предоставляет набор специальных методов-хуков, которые автоматически вызываются фреймворком на разных этапах жизненного цикла компонента или директивы. Эти хуки реализуются через соответствующие интерфейсы, определённые в пакете @angular/core.

Ниже приведены все основные хуки жизненного цикла компонента, порядок их вызова, особенности и практическое применение.

1. constructor()

  • Когда вызывается: при создании экземпляра компонента классом Angular.

  • Что делать: инициализация полей, внедрение зависимостей через DI.

  • Не использовать: доступ к DOM, @Input() значениям или шаблонным переменным — они еще не доступны.

constructor(private service: MyService) {
console.log('constructor');
}

2. ngOnChanges(changes: SimpleChanges)

  • Интерфейс: OnChanges

  • Когда вызывается: при первом присвоении значения @Input() и при каждом последующем его изменении.

  • Что делает: позволяет реагировать на изменение входных данных.

@Input() userId: number;
ngOnChanges(changes: SimpleChanges) {
if (changes\['userId'\]) {
console.log('userId изменился', changes\['userId'\].currentValue);
}
}

Важно: вызывается даже до ngOnInit() при первом присваивании значения.

3. ngOnInit()

  • Интерфейс: OnInit

  • Когда вызывается: один раз после инициализации @Input() параметров и до первого отображения компонента.

  • Что делать: инициализация данных, начало загрузки данных с сервера, подписки.

ngOnInit() {
this.service.loadUser(this.userId).subscribe(...);
}

Отличие от конструктора: ngOnInit() вызывается после установки всех @Input() свойств, constructor() — до.

4. ngDoCheck()

  • Интерфейс: DoCheck

  • Когда вызывается: при каждом цикле обнаружения изменений (change detection).

  • Что делает: позволяет реализовать свою собственную логику отслеживания изменений, альтернативную OnChanges.

ngDoCheck() {
console.log('ngDoCheck вызывается при каждом цикле обнаружения изменений');
}

Часто используется в сочетании с KeyValueDiffers или IterableDiffers для сравнения сложных объектов.

5. ngAfterContentInit()

  • Интерфейс: AfterContentInit

  • Когда вызывается: один раз после вставки контента (проекции) через <ng-content>.

  • Что делать: доступ к контенту, переданному из родителя.

ngAfterContentInit() {
console.log('ngAfterContentInit: контент проецирован');
}

6. ngAfterContentChecked()

  • Интерфейс: AfterContentChecked

  • Когда вызывается: после каждого цикла обнаружения изменений во вставленном контенте.

  • Что делать: логирование или синхронизация состояния с содержимым <ng-content>.

ngAfterContentChecked() {
console.log('ngAfterContentChecked');
}

7. ngAfterViewInit()

  • Интерфейс: AfterViewInit

  • Когда вызывается: один раз после инициализации шаблонных представлений и дочерних компонентов.

  • Что делать: доступ к @ViewChild(), @ViewChildren(), взаимодействие с DOM, инициализация сторонних библиотек.

@ViewChild('myDiv') div!: ElementRef;
ngAfterViewInit() {
console.log(this.div.nativeElement.innerText);
}

Не используйте изменение свойств, влияющих на шаблон — это вызовет ExpressionChangedAfterItHasBeenCheckedError.

8. ngAfterViewChecked()

  • Интерфейс: AfterViewChecked

  • Когда вызывается: после каждого цикла обнаружения изменений во вьюхе.

  • Что делать: синхронизация с вьюхой, производительность, логгинг.

ngAfterViewChecked() {
console.log('ngAfterViewChecked');
}

9. ngOnDestroy()

  • Интерфейс: OnDestroy

  • Когда вызывается: перед уничтожением компонента (удаление из DOM).

  • Что делать: отписка от подписок, очистка таймеров, удаление слушателей, уничтожение сервисов.

private sub!: Subscription;
ngOnInit() {
this.sub = this.service.getData().subscribe(...);
}
ngOnDestroy() {
this.sub.unsubscribe();
console.log('Компонент уничтожен');
}

Порядок вызова хуков

Если представить жизненный цикл как последовательность шагов, порядок будет следующим:

  1. **constructor
    **
  2. **ngOnChanges (если есть @Input())
    **
  3. **ngOnInit
    **
  4. **ngDoCheck
    **
  5. **ngAfterContentInit
    **
  6. **ngAfterContentChecked
    **
  7. **ngAfterViewInit
    **
  8. **ngAfterViewChecked
    **

Далее при каждом цикле change detection вызываются:

  • ngDoCheck

  • ngAfterContentChecked

  • ngAfterViewChecked

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

  • ngOnDestroy

Пример: Полный жизненный цикл

@Component({ ... })
export class DemoComponent implements
OnInit,
OnChanges,
DoCheck,
AfterContentInit,
AfterContentChecked,
AfterViewInit,
AfterViewChecked,
OnDestroy {
@Input() data: any;
constructor() {
console.log('constructor');
}
ngOnChanges(changes: SimpleChanges) {
console.log('ngOnChanges', changes);
}
ngOnInit() {
console.log('ngOnInit');
}
ngDoCheck() {
console.log('ngDoCheck');
}
ngAfterContentInit() {
console.log('ngAfterContentInit');
}
ngAfterContentChecked() {
console.log('ngAfterContentChecked');
}
ngAfterViewInit() {
console.log('ngAfterViewInit');
}
ngAfterViewChecked() {
console.log('ngAfterViewChecked');
}
ngOnDestroy() {
console.log('ngOnDestroy');
}
}

Дополнительные замечания

  • Жизненный цикл запускается повторно при каждом обновлении данных, кроме constructor, ngOnInit, ngAfterViewInit, инициализируемых только один раз.

  • Переупорядочивать методы жизненного цикла нельзя — Angular сам определяет порядок вызова.

  • Интерфейсы не обязательны, но добавляют типизацию и явность.

  • Сокращение нагрузки: избегайте тяжёлых операций в ngDoCheck, ngAfterViewChecked, потому что они вызываются часто.

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