Найти тему
Nuances of programming

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

Оглавление

Источник: Nuances of Programming

Часть 1, Часть 2

Фреймворк Angular был задуман платформенно-независимым. Такой подход позволяет запускать Angular-приложения в разных средах: в браузере, сервере, веб-воркере и даже на мобильных устройствах.

В данной серии статей я опишу, как это вообще возможно — запускать Angular-приложения в разных средах. Мы также научимся создавать пользовательскую платформу Angular, с помощью которой можно визуализировать приложения из терминала, используя графику ASCII.

Любое Angular-приложение начинается с файла main.ts :

-2

Здесь мы создаём новый экземпляр PlatformRef и затем вызываем метод bootstrapModule . Это то место, где запускается Angular-приложение. В этой статье мы попробуем разобраться, как происходит процесс начальной загрузки приложения.

Как было сказано выше, любое Angular-приложение начинается со следующего вызова:

platformRef.bootstrapModule(AppModule)

Вот полный алгоритм метода bootstrapModule:

-3
-4

Обсудим его шаг за шагом.

Содержание

  • Компиляция модуля
  • Корневая Angular-зона
  • Обработка ошибок
  • Инициализаторы
  • Компоненты загрузки

Компиляция модуля

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

-5

Когда мы вызываем метод bootstrapModule(AppModule, options) на PlatformRef , прежде всего, он компилирует этот модуль. Здесь moduleType относится к AppModule. Инжектор – это всего лишь экземпляр Injector, внедрённый через конструктор. А опции есть опции компилятора, задействованные в качестве второго аргумента в методе bootstrapModule.

Чтобы узнать больше о процессе компиляции модуля, рассмотрим поподробнее функцию compileNgModuleFactory.

-6

Во-первых, Angular извлекает экземпляр CompilerFactory из инжектора. CompilerFactory — это абстрактный класс, отвечающий за создание экземпляра Compiler. Например, когда мы запускаем Angular-приложение в режиме разработки, то в качестве реализации выступает JitCompilerFactory . Тогда в результате вызова функции compilerFactory.createCompiler() создаётся такая JitCompiler реализация для компилятора Compiler . Затем этот compiler запрашивается для компилирования нашего AppModule.

-7

Здесь Angular загружает все модули, директивы и конвейеры метаданных. Затем компилирует все компоненты. Во время проведения компиляции компонентов идёт поиск всех метаданных компонентов, зарегистрированных в приложении. Потом Angular обращается к компилятору, чтобы скомпилировать шаблоны всех имеющихся компонентов. Последнее, что нам надо здесь сделать, — это скомпилировать корневой модуль приложения. На этом этапе Angular распознает все требующиеся метаданные для модуля и возвращает фабрику модуля.

Когда компиляция модуля завершается, PlatformRef получает moduleFactory и может начать процесс начальной загрузки.

Root NgZone

Прежде чем осуществлять загрузку Angular-приложения, PlatformRef нужно создать root NgZone.

-8

Root NgZone должен быть инстанцирован ещё до создания AppModule, потому что нам нужно поместить всю логику приложения в эту зону. Между тем, во время создания Angular-модули могут, в свою очередь, активно создавать некоторые провайдеры. Вот почему даже логика создания корневого модуля должна быть помещена внутрь этой зоны.

И только когда создан root NgZone, PlatformRef может инстанцировать корневой модуль через фабрику корневого модуля, появившуюся в качестве результата на этапе компиляции модуля.

Обработка ошибок

Когда root NgZone создан и корневой модуль уже инстанцирован, самое время настроить глобальную обработку ошибок:

-9

ErrorHandler отвечает в Angular за правильную регистрацию и обработку ошибок. Чтобы настроить ErrorHandler, PlatformRef должен извлечь имеющийся ErrorHandler из инжектора, затем отслеживать поток ошибок из корневой зоны и осуществлять вызов метода handlerError, таким образом реагируя на каждое событие ошибки.

Обратите внимание: вся логика обработки ошибок заключена в функцию zone.runOutsideAngular. Эта функция гарантирует, что любой код, выполняемый внутри, не приведёт к запуску обнаружения изменений.

Инициализаторы

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

const initStatus: ApplicationInitStatus = moduleRef.injector.get(ApplicationInitStatus);
initStatus.runInitializers().then(() => {
// ...
});

Здесь Angular использует элемент ApplicationInitStatus, чтобы запустить инициализаторы приложения. Инициализаторы приложения – это функции, исполнение которых требуется непосредственно перед загрузкой приложения. Например, платформа веб-вокера имеет следующий инициализатор:

{provide: APP_INITIALIZER, useValue: setupWebWorker, multi: true}

Таким образом, инициализаторы приложения — это всего лишь функции, представленные токеном APP_INITIALIZER. Здесь все токены APP_INITIALIZER, внедрённые в ApplicationInitStatus, используют следующий оператор:

constructor(@Inject(APP_INITIALIZER) private appInits: (() => any)[]) {

Когда вызывается метод runInitializers, он просто выполняет все инициализаторы приложения и возвращает результат, используя Promise.all().

Компоненты загрузки

На этом этапе PlatformRef завершается со всеми приготовлениями и уже готов к выполнению загрузки AppComponent! Если помните, мы ранее уже видели, как создавался экземпляр корневого модуля:

const moduleRef = moduleFactory.create(this.injector);

Любой корневой Angular-модуль должен содержать массив загрузочных компонентов:

@NgModule({
bootstrap: [AppComponent],
})
export class AppModule {}

PlatformRef просто выполняет перебор значений массива загрузочных компонентов и обращается к ApplicationRef, чтобы осуществить загрузку каждого компонента:

const appRef = injector.get(ApplicationRef);

moduleRef._bootstrapComponents.forEach(f => appRef.bootstrap(f));

ApplicationRef внутри лишь создаёт и визуализирует компоненты:

const componentFactory =
this._componentFactoryResolver.resolveComponentFactory(component);

const compRef = componentFactory.create();

Приведённый выше алгоритм должен быть знаком тем из вас, кто создавал динамические Angular-компоненты. Здесь примечательно то, как был использован ComponentFactoryResolver для того, чтобы распознатьcomponentFactory для AppComponent, а затем просто создать его.

Вот и всё! Мы сделали это! На данном этапе у нас на экран выведен AppComponent, благодаря которому будут визуализированы все остальные части приложения.

Заключение

Поздравляем! Вы добрались до конца статьи, в которой мы вместе прошли процесс начальной загрузки приложения. И теперь у нас есть все необходимые знания, чтобы начать создавать собственную платформу, с помощью которой можно визуализировать Angular-приложения из терминала, используя графику ASCII.

Читайте также:

Читайте нас в телеграмме и vk

Перевод статьи Nikita Poltoratsky: Angular Platforms in depth. Part 2. Application bootstrap process