Основные различия между конструктором и ngOnInit в работе с компонентами Angular

Основные различия между конструктором и ngOnInit в работе с компонентами Angular

От автора: один из самых частых вопросов про Angular компоненты на stackoverflow – чем отличается конструктор от ngOnInit. У данного вопроса более 100к просмотров. Я дал там свой ответ и решил развить его в эту статью. Почти все ответы в треде и статьи в сети сосредоточены на различии в использовании. Я же дам более развернутое сравнение, касающееся процесса инициализации компонентов.

Основные различия между конструктором и ngOnInit в работе с компонентами Angular

Различия в JS/TS языке

Начнем с самого очевидного различия – различия языков. ngOnInit – это лишь метод класса, структурно не отличающийся от других методов класса. Команда Angular решила его так назвать, но у него могло быть совершенно другое название:

class MyComponent {
  ngOnInit() { }
  otherNameForNgOnInit() { }
}

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

export const enum NodeFlags {
  ...
  OnInit = 1 << 16,

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

Практический курс по созданию веб-приложения на Angular4

Станьте профессиональным веб-разработчиком, создавая востребованные веб-приложения на Angular4.

Узнать подробнее
if (def.flags & NodeFlags.OnInit && ...) {
  componentClassInstance.ngOnInit();
}

Конструктор, в свою очередь, совсем другое. Несмотря на то, определили вы его или нет в классе TypeScript, он все равно будет вызываться при создании объекта класса. Это происходит потому, что конструктор класса typescript переводится в JavaScript конструктор:

class MyComponent {
  constructor() {
 console.log('Hello');
  }
}

Переводится в:

function MyComponent() {
  console.log('Hello');
}

Чтобы создать объект класса, эту функцию необходимо вызывать с оператором new:

const componentInstance = new MyComponent(

Если не создавать конструктор в классе, он превратится в пустую функцию:

class MyComponent { }

Переводится в пустую функцию:

function MyComponent() {}

Вот почему я говорю, что конструктор вызывается вне зависимости от того, объявлен ли он в классе или нет.

Различия в процессе инициализации компонентов

Между двумя функциями есть огромное различие в плане инициализации компонентов. Процесс начальной загрузки Angular состоит из двух основных этапов:

Создание дерева компонентов

Запуск обнаружения изменений

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

Практический курс по созданию веб-приложения на Angular4

Станьте профессиональным веб-разработчиком, создавая востребованные веб-приложения на Angular4.

Узнать подробнее

Когда Angular создает дерево компонентов, инъектор корневого модуля уже настроен, т.е. вы можете вставлять любые глобальные зависимости. Когда Angular инициализирует класс дочернего компонента, инъектор родительского компонента уже настроен. Т.е. вы можете вставлять провайдеры, определенные по родительскому компоненту, включая сам родительский компонент. Конструктор компонента лишь метод, вызываемый в контексте инъектора. Если вам нужны зависимости, получить их можно только отсюда. Механизм коммуникации @input обрабатывается как часть следующей фазы обнаружения изменений, поэтому назначение входных данных не доступно в конструкторе.

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

Более подробно об обнаружении изменений можете прочитать в статье «все что нужно знать об обнаружении изменений в Angular», а о том, как Angular обрабатывает входные данные в статье «механика обновления назначений свойств в Angular».

Продемонстрируем эти фазы небольшим примером. Предположим, у вас следующий шаблон:

<my-app>
 <child-comp ='prop'>

Angular начинает предварительную загрузку приложения. Как сказано выше, сначала он создает классы для всех компонентов. То есть вызывается конструктор MyAppComponent. При выполнении конструктора компонента Angular разрешает все зависимости, встроенные в конструктор MyAppComponent, и передает их в виде параметров. Также создается узел DOM, который будет хранить компонент my-app. Далее создается хост элемент для child-comp, и вызывается конструктор ChildComponent. На этом этапе Angular не связан с привязкой входных данных и хуками жизненного цикла. Т.е. после завершения процесса Angular имеет следующее дерево компонентов:

MyAppView
  - MyApp component instance
  - my-app host element data
 ChildComponentView
 - ChildComponent component instance
 - child-comp host element data

Только после этого Angular запускает обнаружение изменений и обновляет назначения для my-app и вызывает ngOnInit на объекте MyAppComponent. Далее происходит переход к обновлению назначений для child-comp и вызову ngOnInit на классе ChildComponent.

Более подробно о виде, о котором я говорил выше, можно прочитать в статье «вот почему вы не найдете компоненты в Angular».

Различия в использовании

Давайте разберем, в чем различие с точки зрения использования.

Конструктор

Конструктор класса в Angular почти всегда используется для вставки зависимостей. Angular вызывает constructor injection pattern, который подробно разбирался здесь. Более подробно о его архитектуре можно прочитать в статье «инъекция в конструкторе или инъекция в сеттере» от Miško Hevery.

Тем не менее, использование конструктора не ограничено DI. Например, директива router-outlet модуля @angular/router использует его для своей регистрации и определения своего положения (viewContainerRef) внутри экосистемы роутера. Я описывал этот подход в статье «вот как можно получить ViewContainerRef до обработки запроса @ViewChild».

Старайтесь писать как можно меньше логики в конструкторе.

NgOnInit

Как мы уже знаем, когда Angular вызывает ngOnInit, он уже построил DOM компонента, вставил все необходимые зависимости через конструктор и обработал назначения входных данных. Здесь хранится все необходимая информация, что делает этот метод подходящим местом для выполнения логики инициализации.

Старайтесь использовать ngOnInit для выполнения логики инициализации, даже если эта логика не зависит от DI, DOM и назначений входа.

Автор: Maxim Koretskyi

Источник: https://blog.angularindepth.com

Редакция: Команда webformyself.

Практический курс по созданию веб-приложения на Angular4

Станьте профессиональным веб-разработчиком, создавая востребованные веб-приложения на Angular4.

Узнать подробнее
Самые свежие новости IT и веб-разработки на нашем Telegram-канале

Angular 4 с Нуля до Профи

Angular 4 - полное руководство для современной веб-разработки

Научиться

Метки:

Похожие статьи:

Комментарии Вконтакте:

Комментарии Facebook:

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Можно использовать следующие HTML-теги и атрибуты: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Я не робот.

Spam Protection by WP-SpamFree