Angular 21 — Фаза 2: Bootstrap (старт приложения)
Версия 21.0.3, standalone + zoneless. Точка входа src/main.ts:
bootstrapApplication(App, appConfig).catch(err => console.error(err));
Прослежено по исходникам src-v21 (а не по статьям) — пути указаны для каждого шага.
Цепочка вызовов (сверху вниз)
main.ts: bootstrapApplication(App, appConfig)
platform-browser/src/browser.ts:127
└─ (JIT-режим: сначала resolveComponentResources — подтянуть внешние templateUrl/styleUrl)
└─ internalCreateApplication({ rootComponent, appProviders, platformProviders })
core/src/application/create_application.ts:38
├─ createOrReusePlatformInjector(platformProviders) // платформенный injector
├─ allAppProviders = [
│ provideZonelessChangeDetectionInternal(), // ← zoneless ПО УМОЛЧАНИЮ (v21!)
│ errorHandlerEnvironmentInitializer,
│ ...appProviders // наши provideRouter и т.д.
│ ]
├─ new EnvironmentNgModuleRefAdapter({...}) // создаёт EnvironmentInjector
└─ bootstrap({ r3Injector, platformInjector, rootComponent })
core/src/platform/bootstrap.ts:81
└─ ngZone.run(() => { // в zoneless это NoopNgZone
r3Injector.resolveInjectorInitializers() // ENVIRONMENT_INITIALIZER
initStatus.runInitializers() // APP_INITIALIZER (provideAppInitializer)
await initStatus.donePromise
appRef = injector.get(ApplicationRef)
appRef.bootstrap(rootComponent) // ← создаёт и рендерит корневой компонент
return appRef
})
Ключевые объекты, которые создаются
- Platform injector — самый верхний DI-контейнер (один на страницу).
createOrReusePlatformInjector(create_application.ts). Содержит общие для платформы сервисы. - Environment injector (root) — контейнер уровня приложения. Сюда попадают
все провайдеры из
appConfig.providersплюс служебные. Реализация —R3Injector(core/src/di/r3_injector.ts). Создаётся черезEnvironmentNgModuleRefAdapter. - ApplicationRef — «дирижёр» приложения (
core/src/application/application_ref.ts). Держит список view (allViews), флаги «грязности» (dirtyFlags) и методtick().
Порядок инициализации (важно)
Внутри bootstrap() строго:
resolveInjectorInitializers()— выполняетENVIRONMENT_INITIALIZER(например, внутренние установки роутера).initStatus.runInitializers()— выполняетAPP_INITIALIZER(в т.ч. нашprovideAppInitializer(() => ...)). Если инициализатор вернул Promise — bootstrap ждёт его черезinitStatus.donePromise.- Только после готовности инициализаторов —
appRef.bootstrap(rootComponent).
Это объясняет, почему APP_INITIALIZER гарантированно отрабатывает до первого
рендера компонента.
appRef.bootstrap(App) — рождение корневого компонента
ApplicationRef.bootstrap (application_ref.ts):
- по
App.ɵcmp(тот самыйdefineComponentиз фазы Compile) создаётComponentRefчерезComponentFactory/createRootComponent; - находит в DOM хост-элемент по селектору
app-root; - выполняет create-проход template-функции (
rf & 1) → строится DOM; - регистрирует view в
ApplicationRef.allViews; - помечает приложение «грязным» и инициирует первый
tick()→ первый update-проход (rf & 2) заполняет интерполяции.
Профайлер на старте даёт события (порядок из enum ProfilerEvent):
BootstrapApplicationStart → … → BootstrapComponentStart → ComponentStart → TemplateCreateStart/End (создание DOM) → TemplateUpdateStart/End (первые биндинги) → ComponentEnd → BootstrapComponentEnd → AfterRenderHooks → BootstrapApplicationEnd.
Роль NgZone в zoneless
bootstrap() всё равно вызывает ngZone.run(...), но при zoneless NgZone — это
NoopNgZone (ничего не патчит). Change detection теперь запускается не «по любому
async», а через scheduler, реагирующий на изменения сигналов и явные
markForCheck/ApplicationRef.tick (см. фазу Runtime). Проверка на конфликт:
если поданы и provideZoneChangeDetection, и zoneless — в dev будет warning
(bootstrap.ts:96).
Привязка к исходникам
packages/platform-browser/src/browser.ts—bootstrapApplicationpackages/core/src/application/create_application.ts—internalCreateApplicationpackages/core/src/platform/bootstrap.ts—bootstrap(инициализаторы, NgZone)packages/core/src/application/application_ref.ts—ApplicationRef,bootstrap,tickpackages/core/src/di/r3_injector.ts—R3Injector(Environment injector)
Итог фазы Bootstrap
bootstrapApplication→internalCreateApplicationсобирает провайдеры (вкл. zoneless по умолчанию) и создаёт два инжектора: platform и environment.bootstrap()запускает инициализаторы (ENVIRONMENT → APP_INITIALIZER) и ждёт их.ApplicationRef.bootstrap(App)создаётComponentRef, выполняет create-проход (строит DOM) и первыйtick(update-проход, заполняет биндинги).- Дальше приложение живёт реактивно — см.
03-runtime.md.