一個最簡單的Angular程序
在了解Angular2啟動過程之前,我們先看一段代碼:
import { platformBrowserDynamic } from "@angular/platform-browser-dynamic";
import { NgModule, Component} from "@angular/core";
import { BrowserModule } from "@angular/platform-browser";
// 定義Component
@Component({
selector: "my-app",
template: "<div></div>"
})
export class AppComponent {}
// 定義Module
@NgModule({
imports: [
BrowserModule
],
declarations: [
AppComponent
],
bootstrap: [ AppComponent ]
})
export class AppModule { }
// 啟動Angular程序
platformBrowserDynamic().bootstrapModule(AppModule);
這一段代碼主要有四個部分組成:
1、引用Angular相關(guān)的類
2、定義Component(什么是Component(todo:下次分析),@Component是什么(在這里))
3、定義了一個Module(什么是Module(todo:下次分析),@Module是什么(在這里))
4、調(diào)用Angular的啟動函數(shù),實現(xiàn)啟動功能。
從代碼我們可以發(fā)現(xiàn),啟動過程實際上是分成了兩大步驟的。
1、調(diào)用platformBrowserDynamic()方法
2、調(diào)用bootstrapModule(AppModule)方法(在這里)
platformBrowserDynamic都干了什么
要想知道platformBrowserDynamic都干了啥,我們可以查看他的代碼:
export const platformBrowserDynamic = createPlatformFactory(
platformCoreDynamic, 'browserDynamic', INTERNAL_BROWSER_DYNAMIC_PLATFORM_PROVIDERS);
可以看出這個方法是通過調(diào)用createPlatformFactory方法得到的,生成的時候會傳入三個參數(shù),其中有第二個參數(shù)是字符串,我們看看其它兩個參數(shù)都是什么。
第一個參數(shù):
export const platformCoreDynamic = createPlatformFactory(platformCore, 'coreDynamic', [
{provide: COMPILER_OPTIONS, useValue: {}, multi: true},
{provide: CompilerFactory, useClass: JitCompilerFactory},
{provide: PLATFORM_INITIALIZER, useValue: _initReflector, multi: true},
]);
第三個參數(shù):
export const INTERNAL_BROWSER_DYNAMIC_PLATFORM_PROVIDERS: Provider[] = [
INTERNAL_BROWSER_PLATFORM_PROVIDERS,
{
provide: COMPILER_OPTIONS,
useValue: {providers: [{provide: ResourceLoader, useClass: ResourceLoaderImpl}]},
multi: true
},
];
發(fā)現(xiàn)platformCoreDynamic也是通過createPlatformFactory方法得到的,調(diào)用的時候又引入了一個新的參數(shù):platformCore,它初始化的代碼為:
function _reflector(): Reflector {
return reflector;
}
const _CORE_PLATFORM_PROVIDERS: Provider[] = [
PlatformRef_,
{provide: PlatformRef, useExisting: PlatformRef_},
{provide: Reflector, useFactory: _reflector, deps: []},
{provide: ReflectorReader, useExisting: Reflector},
TestabilityRegistry,
Console,
];
/**
* This platform has to be included in any other platform
*
* @experimental
*/
export const platformCore = createPlatformFactory(null, 'core', _CORE_PLATFORM_PROVIDERS);
可以看出platformCore初始化的時候也是調(diào)用的createPlatformFactory。
至此我們可以知道platformBrowserDynamic的關(guān)系圖:
platformBrowserDynamic
|
+---> platformCoreDynamic
|
+---> platformCore
接下來我們分析最為關(guān)鍵的createPlatformFactory方法。
export function createPlatformFactory(
parentPlaformFactory: (extraProviders?: Provider[]) => PlatformRef, name: string,
providers: Provider[] = []): (extraProviders?: Provider[]) => PlatformRef {
const marker = new OpaqueToken(`Platform: ${name}`);
return (extraProviders: Provider[] = []) => {
if (!getPlatform()) {
if (parentPlaformFactory) {
parentPlaformFactory(
providers.concat(extraProviders).concat({provide: marker, useValue: true}));
} else {
createPlatform(ReflectiveInjector.resolveAndCreate(
providers.concat(extraProviders).concat({provide: marker, useValue: true})));
}
}
return assertPlatform(marker);
};
}
可以看出在createPlatformFactory方法中,只是簡單的返回了一個內(nèi)部方法。當我們調(diào)用platformBrowserDynamic()時,實際就是調(diào)用這個內(nèi)部方法。其實這個內(nèi)部方法也比較簡單,主要邏輯為:
1、判斷是否已經(jīng)創(chuàng)建過了。
2、判斷是否有父Factory。
3、如果有父Factory就把調(diào)用Factory時傳入的Provider和調(diào)用createPlatformFactory傳入的Provider合并,然后調(diào)用父Factory。
4、如果沒有父Factory,先創(chuàng)建一個Injector,然后去創(chuàng)建platform。
由之前的關(guān)系圖可以知道,當我們調(diào)用platformBrowserDynamic,實際調(diào)用的是platformCore,Provider就是各個createPlatformFactory傳入的第三個參數(shù),通過這些Provider創(chuàng)建了一個Injector,并通過Injector創(chuàng)建了platform(看這里)。
我們在看看創(chuàng)建Platform的過程:
export function createPlatform(injector: Injector): PlatformRef {
if (_platform && !_platform.destroyed) {
throw new Error(
'There can be only one platform. Destroy the previous one to create a new one.');
}
_platform = injector.get(PlatformRef);
const inits: Function[] = <Function[]>injector.get(PLATFORM_INITIALIZER, null);
if (inits) inits.forEach(init => init());
return _platform;
}
在這段代碼使用到了Injector的特性來創(chuàng)建的(看這里)。創(chuàng)建完以后會獲取所有名為PLATFORM_INITIALIZER的Provider,然后調(diào)用它。
至此platformBrowserDynamic()執(zhí)行過程已經(jīng)全部理清楚了,在這個過程中會創(chuàng)建Injector和platform,其中Injector是依賴注入的關(guān)鍵(看這里)。
在分析的過程中,發(fā)現(xiàn)可以通過定義一個PLATFORM_INITIALIZER名稱的Provider實現(xiàn)在初始化Platform后執(zhí)行代碼。示例代碼:
function test(){
console.log("This is a test!");
}
platformBrowserDynamic([{provide: PLATFORM_INITIALIZER, useValue: test, multi: true}]).bootstrapModule(AppModule);