В чём разница между ngOnInit и constructor?
В Angular constructor и ngOnInit — это два разных этапа жизненного цикла компонента, выполняющих разные задачи. Они оба вызываются при создании компонента, но предназначены для различных целей. Понимание их различий важно для правильной архитектуры компонентов, корректной инициализации данных и обеспечения совместимости с Angular-фреймворком.
constructor
Что это
constructor — это стандартный метод класса в TypeScript (или JavaScript), вызываемый при создании нового экземпляра класса. Он не является частью Angular lifecycle, а частью самого языка.
Основные задачи
-
Внедрение зависимостей через Angular Dependency Injection.
-
Инициализация полей класса, которые не зависят от Angular.
-
Подготовка базового состояния объекта.
Особенности
-
Angular вызывает constructor до того, как выполнится инициализация Angular-специфичных механизмов, таких как @Input(), @ViewChild, ngOnInit и другие.
-
В constructor нельзя безопасно обращаться к DOM или входным параметрам компонента.
-
Подходит только для базовой инициализации, не связанной с Angular-инфраструктурой.
Пример
@Component({...})
export class MyComponent {
constructor(private http: HttpClient) {
console.log('constructor called');
}
}
ngOnInit
Что это
ngOnInit — это специальный метод Angular, определённый интерфейсом OnInit. Он вызывается фреймворком после инициализации всех входных свойств компонента (включая @Input()) и до первого отображения компонента в DOM.
Основные задачи
-
Инициализация данных, зависящих от входных параметров.
-
Запуск загрузки данных с сервера или других async-операций.
-
Подключение подписок, выполнение логики, зависящей от шаблона.
Особенности
-
Angular гарантирует, что к моменту вызова ngOnInit:
-
Все @Input() свойства имеют значения.
-
Компонент включён в дерево Angular.
-
-
Может безопасно обращаться к @Input(), @ViewChild, @ContentChild и другим Angular-специфичным возможностям.
Пример
@Component({...})
export class MyComponent implements OnInit {
@Input() userId!: number;
constructor(private http: HttpClient) {}
ngOnInit() {
// Здесь можно использовать userId
this.http.get(\`/api/users/${this.userId}\`).subscribe(...);
}
}
Сравнение по критериям
Критерий | constructor | ngOnInit |
---|---|---|
Тип | Стандартный метод TypeScript | Angular lifecycle hook |
--- | --- | --- |
Когда вызывается | Сразу при создании экземпляра класса | После инициализации @Input() |
--- | --- | --- |
Доступ к @Input() | ❌ Нет | ✅ Да |
--- | --- | --- |
Доступ к DOM (@ViewChild) | ❌ Нет | ✅ Да (после ngAfterViewInit) |
--- | --- | --- |
Используется Angular | ❌ Нет | ✅ Да |
--- | --- | --- |
Где выполнять HTTP-запросы | Не рекомендуется | Рекомендуется |
--- | --- | --- |
Где инициализировать переменные | Только те, что не зависят от @Input() или DOM | Все, что зависит от Angular |
--- | --- | --- |
Частота вызова | Один раз при создании | Один раз при инициализации в жизненном цикле |
--- | --- | --- |
Почему нельзя всё делать в constructor
Angular вызывает constructor до того, как Angular успеет передать значения в @Input() свойства. Это значит, что попытка использовать @Input() внутри constructor приведёт к тому, что вы получите undefined или некорректные значения.
Также, если в constructor будет логика, обращающаяся к DOM (например, через @ViewChild), она не будет работать, потому что элемент ещё не отображён.
Практический шаблон использования
@Component({...})
export class UserProfileComponent implements OnInit {
@Input() userId!: string;
userData: any;
constructor(private userService: UserService) {
// Только внедрение зависимостей
}
ngOnInit() {
// Здесь безопасно использовать userId
this.userService.getUser(this.userId).subscribe(data => {
this.userData = data;
});
}
}
Использование constructor и ngOnInit вместе
Это обычная практика: constructor используется только для DI и базовой инициализации, а вся логика, зависящая от Angular, переносится в ngOnInit.
constructor(private logger: LoggerService) {
this.logger.log('Component instance created');
}
ngOnInit() {
this.loadData();
}
Под капотом
Когда Angular создает компонент:
-
Создает экземпляр класса (вызывает constructor).
-
Устанавливает все @Input() значения.
-
Вызывает ngOnChanges() (если реализован и есть @Input()).
-
Вызывает ngOnInit().
Это означает, что ngOnInit — первая точка, где компонент полностью готов к работе с Angular-инфраструктурой.
Ошибки при неправильном использовании
@Component({...})
export class ExampleComponent {
@Input() data!: string;
constructor() {
console.log(this.data); // ❌ undefined
}
}
Это вызовет undefined, потому что data ещё не установлено. Правильное место:
ngOnInit() {
console.log(this.data); // ✅ корректное значение
}
constructor и ngOnInit не конкурируют между собой — они дополняют друг друга. Правильное распределение обязанностей между ними — важный навык при разработке Angular-приложений.