Источник: Nuances of Programming
Часть 1, Часть 2
Фреймворк Angular был задуман платформенно-независимым. Такой подход позволяет запускать Angular-приложения в разных средах: в браузере, сервере, веб-воркере и даже на мобильных устройствах.
В данной серии статей я опишу, как это вообще возможно — запускать Angular-приложения в разных средах. Мы также научимся создавать пользовательскую платформу Angular, с помощью которой можно визуализировать приложения из терминала, используя графику ASCII.
Любое Angular-приложение начинается с файла main.ts :
Здесь мы создаём новый экземпляр PlatformRef и затем вызываем метод bootstrapModule . Это то место, где запускается Angular-приложение. В этой статье мы попробуем разобраться, как происходит процесс начальной загрузки приложения.
Как было сказано выше, любое Angular-приложение начинается со следующего вызова:
platformRef.bootstrapModule(AppModule)
Вот полный алгоритм метода bootstrapModule:
Обсудим его шаг за шагом.
Содержание
- Компиляция модуля
- Корневая Angular-зона
- Обработка ошибок
- Инициализаторы
- Компоненты загрузки
Компиляция модуля
Первый этап в процессе начальной загрузки приложения — это компиляция модуля.
Когда мы вызываем метод bootstrapModule(AppModule, options) на PlatformRef , прежде всего, он компилирует этот модуль. Здесь moduleType относится к AppModule. Инжектор – это всего лишь экземпляр Injector, внедрённый через конструктор. А опции есть опции компилятора, задействованные в качестве второго аргумента в методе bootstrapModule.
Чтобы узнать больше о процессе компиляции модуля, рассмотрим поподробнее функцию compileNgModuleFactory.
Во-первых, Angular извлекает экземпляр CompilerFactory из инжектора. CompilerFactory — это абстрактный класс, отвечающий за создание экземпляра Compiler. Например, когда мы запускаем Angular-приложение в режиме разработки, то в качестве реализации выступает JitCompilerFactory . Тогда в результате вызова функции compilerFactory.createCompiler() создаётся такая JitCompiler реализация для компилятора Compiler . Затем этот compiler запрашивается для компилирования нашего AppModule.
Здесь Angular загружает все модули, директивы и конвейеры метаданных. Затем компилирует все компоненты. Во время проведения компиляции компонентов идёт поиск всех метаданных компонентов, зарегистрированных в приложении. Потом Angular обращается к компилятору, чтобы скомпилировать шаблоны всех имеющихся компонентов. Последнее, что нам надо здесь сделать, — это скомпилировать корневой модуль приложения. На этом этапе Angular распознает все требующиеся метаданные для модуля и возвращает фабрику модуля.
Когда компиляция модуля завершается, PlatformRef получает moduleFactory и может начать процесс начальной загрузки.
Root NgZone
Прежде чем осуществлять загрузку Angular-приложения, PlatformRef нужно создать root NgZone.
Root NgZone должен быть инстанцирован ещё до создания AppModule, потому что нам нужно поместить всю логику приложения в эту зону. Между тем, во время создания Angular-модули могут, в свою очередь, активно создавать некоторые провайдеры. Вот почему даже логика создания корневого модуля должна быть помещена внутрь этой зоны.
И только когда создан root NgZone, PlatformRef может инстанцировать корневой модуль через фабрику корневого модуля, появившуюся в качестве результата на этапе компиляции модуля.
Обработка ошибок
Когда root NgZone создан и корневой модуль уже инстанцирован, самое время настроить глобальную обработку ошибок:
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