angular平臺的實例化過程

cli生成項目後,main.ts中的代碼

import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';

import { AppModule } from './app/app.module';
import { environment } from './environments/environment';

if (environment.production) {
  enableProdMode();
}

platformBrowserDynamic().bootstrapModule(AppModule).catch(err => console.error(err));

這裏調用了@angular/platform-browser-dynamic包中導出的platformBrowserDynamic函數,這個函數是瀏覽器平臺的工廠函數,執行會返回瀏覽器平臺的實例

export const platformBrowserDynamic = createPlatformFactory(platformCoreDynamic, 'browserDynamic', INTERNAL_BROWSER_DYNAMIC_PLATFORM_PROVIDERS);

platformBrowserDynamic函數是通過createPlatformFactory函數創建的,這個函數接收3個參數,parentPlatformFactory(父平臺工廠函數),name(平臺名稱),providers(服務提供商的數組)
<!-- 顧名思義,createPlatformFactory函數的作用是創建平臺工廠的函數 -->

export function createPlatformFactory(
    parentPlatformFactory: ((extraProviders?: StaticProvider[]) => PlatformRef) | null,
    name: string, providers: StaticProvider[] = []): (extraProviders?: StaticProvider[]) =>
    PlatformRef {
  const desc = `Platform: ${name}`;
  const marker = new InjectionToken(desc);
  return (extraProviders: StaticProvider[] = []) => {
    let platform = getPlatform();
    if (!platform || platform.injector.get(ALLOW_MULTIPLE_PLATFORMS, false)) {
      if (parentPlatformFactory) {
        parentPlatformFactory(
            providers.concat(extraProviders).concat({provide: marker, useValue: true}));
      } else {
        const injectedProviders: StaticProvider[] =
            providers.concat(extraProviders).concat({provide: marker, useValue: true});
        createPlatform(Injector.create({providers: injectedProviders, name: desc}));
      }
    }
    return assertPlatform(marker);
  };
}

在angular框架被加載後,會執行這個函數,並傳入了三個參數分別爲 platformCoreDynamic, 'browserDynamic', INTERNAL_BROWSER_DYNAMIC_PLATFORM_PROVIDERS

看第一個參數platformCoreDynamic:

export const platformCoreDynamic = createPlatformFactory(platformCore, 'coreDynamic', [
    {provide: COMPILER_OPTIONS, useValue: {}, multi: true},
    {provide: CompilerFactory, useClass: JitCompilerFactory, deps: [COMPILER_OPTIONS]},
]);

第一個參數也是通過createPlatformFactory函數創建的一個工廠函數,這裏執行的時候又傳入了三個參數platformCore,'coreDynamic'和一個提供商數組

第一個參數platformCore:

export const platformCore = createPlatformFactory(null, 'core', _CORE_PLATFORM_PROVIDERS);

又是通過createPlatformFactory函數創建的... ,但好在沒有在繼續傳入父平臺作爲參數,所以應用初始化時執行的第一個函數就是這個了

這裏有點繞,屢一下函數的執行過程:

  • createPlatformFactory(null, 'core', _CORE_PLATFORM_PROVIDERS)執行返回platformCore函數
  • createPlatformFactory(platformCore, 'coreDynamic', [...])執行返回platformCoreDynamic函數
  • createPlatformFactory(platformCoreDynamic, 'browserDynamic', INTERNAL_BROWSER_DYNAMIC_PLATFORM_PROVIDERS)執行返回platformBrowserDynamic函數
  • platformBrowserDynamic()執行返回平臺實例

要注意到這裏創建的都是工廠函數,而不是平臺實例,在angular框架被加載後,就會開始執行,此時應用還沒有正式啓動

在執行platformBrowserDynamic()後,應用開始啓動,實例化core平臺(這裏的調用順序就不貼出來了,雖然工廠函數的調用順序是platformBrowserDynamic->platformCoreDynamic->platformCore,但是實例化的只有core平臺)

coreDynamic平臺和browserDynamic平臺的工廠函數並不是創建子平臺的實例,而是添加服務提供商,被實例化的只有一個平臺實例,只不過會改變PLATFORM_IDtoken的值

core平臺實例化之前,首先創建了應用的根注入器

createPlatform(Injector.create({providers: injectedProviders, name: desc}));

core平臺實例化過程中,又通過子平臺工廠函數的參數和區域變量,傳入了一些服務提供商,然後將這些提供商統一註冊到了注入器中:

// 這裏的變量名是我命名的不是源碼中的名字,用以區分各個平臺下注冊的提供商
export const browserDynamic_PROVIDERS: StaticProvider[] = [
  {provide: PLATFORM_ID, useValue: PLATFORM_BROWSER_ID},
  {provide: PLATFORM_INITIALIZER, useValue: initDomAdapter, multi: true},
  {provide: PlatformLocation, useClass: BrowserPlatformLocation, deps: [DOCUMENT]},
  {provide: DOCUMENT, useFactory: _document, deps: []},
  {
    provide: COMPILER_OPTIONS,
    useValue: {providers: [{provide: ResourceLoader, useClass: ResourceLoaderImpl, deps: []}]},
    multi: true
  },
  {provide: PLATFORM_ID, useValue: PLATFORM_BROWSER_ID}
];

export const coreDynamic_PROVIDERS: StaticProvider[] = [
  {provide: COMPILER_OPTIONS, useValue: {}, multi: true},
  {provide: CompilerFactory, useClass: JitCompilerFactory, deps: [COMPILER_OPTIONS]}
];

export const core_PROVIDERS: StaticProvider[] = [
  // Set a default platform name for platforms that don't set it explicitly.
  {provide: PLATFORM_ID, useValue: 'unknown'},
  {provide: PlatformRef, deps: [Injector]},
  {provide: TestabilityRegistry, deps: []},
  {provide: Console, deps: []},

然後進行初始化的操作,包括BrowserDomAdapter(DOM適配器)和BrowserGetTestability

export function createPlatform(injector: Injector): PlatformRef {
  if (_platform && !_platform.destroyed &&
      !_platform.injector.get(ALLOW_MULTIPLE_PLATFORMS, false)) {
    throw new Error(
        'There can be only one platform. Destroy the previous one to create a new one.');
  }
  _platform = injector.get(PlatformRef);
  // 初始化操作 browserDynamic平臺下的PLATFORM_INITIALIZER服務
  const inits = injector.get(PLATFORM_INITIALIZER, null);
  if (inits) inits.forEach((init: any) => init());
  return _platform;
}

這裏初始化操作是通過PLATFORM_INITIALIZERtoken注入,然後遍歷執行,所以也可以在應用中注入PLATFORM_INITIALIZER,然後執行一些啓動時的自定義的任務.

到這裏,core平臺的實例化就完成了.

ps:

除了platform-browser-dynamic之外還有platform-browser模塊,這兩個模塊的區別是渲染方式的不同,platform-browser-dynamic模塊提供jit編譯,也就是說編譯在瀏覽器內完成,而platform-browser模塊提供aot編譯,編譯在本地完成.

在代碼層面上來說,platform-browser模塊下core平臺的子平臺只有browser平臺,而platform-browser-dynamic模塊下,core平臺的子平臺包含coreDynamic平臺和browserDynamic平臺,並添加了額外的服務提供商

  {
    provide: COMPILER_OPTIONS,
    useValue: {providers: [{provide: ResourceLoader, useClass: ResourceLoaderImpl, deps: []}]},
    multi: true
  },
  {provide: CompilerFactory, useClass: JitCompilerFactory, deps: [COMPILER_OPTIONS]},
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章