直接看人話總結git
今天 angularv8的正式版發了,可是除了路由懶加載那裏沒以爲有啥大變化,有點小失望github
承接上一章typescript
該文章項目地址bootstrap
文章地址api
試讀angular源碼第一章:開場與platformBrowserDynamicapp
試讀angular源碼第二章:引導模塊bootstrapModule框架
試讀angular源碼第四章:angular模塊及JIT編譯模塊
Angular 應用是模塊化的,它擁有本身的模塊化系統,稱做 NgModule
。
一個 NgModule
就是一個容器,用於存放一些內聚的代碼塊,這些代碼塊專一於某個應用領域、某個工做流或一組緊密相關的功能。
它能夠包含一些組件、服務提供商或其它代碼文件,其做用域由包含它們的 NgModule
定義。 它還能夠導入一些由其它模塊中導出的功能,並導出一些指定的功能供其它 NgModule
使用。
每一個 Angular 應用都至少有一個 NgModule
類,也就是根模塊,它習慣上命名爲 AppModule
,並位於一個名叫 app.module.ts
的文件中。
引導這個根模塊就能夠啓動你的應用。
當 bootstrap(引導)根模塊以後,NgModule
會繼而實例化元數據中 bootstrap
。
bootstrap 應用的主視圖,稱爲根組件。它是應用中全部其它視圖的宿主。只有根模塊才應該設置這個 bootstrap
屬性
bootstrapModule
是在上一節 platformBrowserDynamic()
返回的平臺實例 PlatformRef
中的一個方法,用於引導啓動實例根模塊。
angular/packages/core/src/application_ref.ts
@Injectable()
export class PlatformRef {
...
bootstrapModule<M>(
moduleType: Type<M>, compilerOptions: (CompilerOptions&BootstrapOptions)|
Array<CompilerOptions&BootstrapOptions> = []): Promise<NgModuleRef<M>> {
// 註釋:bootstrapModule` 首先經過 `optionsReducer` 遞歸 reduce 將編譯器選項 `compilerOptions` 拍平爲對象
const options = optionsReducer({}, compilerOptions);
// 註釋:這裏獲取到編譯後的模塊工廠,而後返回給 bootstrapModuleFactory建立模塊
return compileNgModuleFactory(this.injector, options, moduleType)
.then(moduleFactory => this.bootstrapModuleFactory(moduleFactory, options));
}
...
}
複製代碼
bootstrapModule
接受2個參數:
moduleType: Type<M>
根模塊compilerOptions: (CompilerOptions&BootstrapOptions)| Array<CompilerOptions&BootstrapOptions> = []
編譯器選項,默認是空數組這裏有個頗有意思的 typescript 寫法:Type<M>
:
angular/packages/core/src/interface/type.ts
export interface Type<T> extends Function { new (...args: any[]): T; }
複製代碼
接口 Type
繼承 Function
,其實 Type<T>
能夠說是 class
的類型。
在這裏,bootstrapModule
:
optionsReducer
遞歸 reduce 將編譯器選項 compilerOptions
拍平爲對象。compileNgModuleFactory
傳入平臺實例的注射器 injector
,編譯器選項和要引導實例化的根模塊。angular/packages/core/src/application_ref.ts
let compileNgModuleFactory:
<M>(injector: Injector, options: CompilerOptions, moduleType: Type<M>) =>
Promise<NgModuleFactory<M>> = compileNgModuleFactory__PRE_R3__;
function compileNgModuleFactory__PRE_R3__<M>( injector: Injector, options: CompilerOptions, moduleType: Type<M>): Promise<NgModuleFactory<M>> {
// 註釋:其實就是平臺coreDynamic 的服務商 JitCompilerFactory
const compilerFactory: CompilerFactory = injector.get(CompilerFactory);
// 註釋:調用 JitCompilerFactory 建立編譯器實例 CompilerImpl
const compiler = compilerFactory.createCompiler([options]);
// 註釋:異步建立 ngmodule 模塊工廠 (CompilerImpl 經過代理 CompilerImpl 去編譯)
return compiler.compileModuleAsync(moduleType);
}
複製代碼
compileNgModuleFactory
在這裏其實就是 compileNgModuleFactory__PRE_R3__
PlatformRef
的注射器 injector
獲取了編譯器實例,其實也就是 coreDynamic
提供的 JitCompilerFactory
JitCompilerFactory
的 createCompiler
方法,建立編譯器 Compiler
實例 CompilerImpl
Compiler
實例 CompilerImpl
異步編譯給定的 NgModule
及其全部組件coreDynamic
提供的 JitCompilerFactory
調用 createCompiler
建立編譯器實例 Compiler
的時候,實際上是在這裏注入的服務供應商 CompilerImpl
,
因此最後建立了的編譯器實例 Compiler
實際上是 CompilerImpl
。
angular/packages/platform-browser-dynamic/src/compiler_factory.ts
{ provide: Compiler, useClass: CompilerImpl, deps: [Injector, CompileMetadataResolver....]}
複製代碼
angular/packages/platform-browser-dynamic/src/compiler_factory.ts
export class CompilerImpl implements Compiler {
private _delegate: JitCompiler;
public readonly injector: Injector;
constructor( injector: Injector, private _metadataResolver: CompileMetadataResolver, templateParser: TemplateParser, styleCompiler: StyleCompiler, viewCompiler: ViewCompiler, ngModuleCompiler: NgModuleCompiler, summaryResolver: SummaryResolver<Type<any>>, compileReflector: CompileReflector, jitEvaluator: JitEvaluator, compilerConfig: CompilerConfig, console: Console) {
// 註釋:建立 JIT 編譯器
this._delegate = new JitCompiler(
_metadataResolver, templateParser, styleCompiler, viewCompiler, ngModuleCompiler,
summaryResolver, compileReflector, jitEvaluator, compilerConfig, console,
this.getExtraNgModuleProviders.bind(this));
this.injector = injector;
}
...
// 註釋:異步建立模塊及其子組件
compileModuleAsync<T>(moduleType: Type<T>): Promise<NgModuleFactory<T>> {
return this._delegate.compileModuleAsync(moduleType) as Promise<NgModuleFactory<T>>;
}
...
}
複製代碼
因此 compileNgModuleFactory
在異步建立模塊工廠和組件 compiler.compileModuleAsync(moduleType)
時,其實調用的是 CompilerImpl
實例 的 compileModuleAsync
。
而在 JTT 編譯器實例化的時候,會實例一個 JitCompiler
當作代理去編譯,因此實際上異步建立模塊工廠和組件這個方法具體是由 JitCompiler
實例的方法 compileModuleAsync
執行的:
angular/packages/compiler/src/jit/compiler.ts
export class JitCompiler {
private _compiledTemplateCache = new Map<Type, CompiledTemplate>();
private _compiledHostTemplateCache = new Map<Type, CompiledTemplate>();
private _compiledDirectiveWrapperCache = new Map<Type, Type>();
private _compiledNgModuleCache = new Map<Type, object>();
private _sharedStylesheetCount = 0;
private _addedAotSummaries = new Set<() => any[]>();
constructor(
private _metadataResolver: CompileMetadataResolver, private _templateParser: TemplateParser,
private _styleCompiler: StyleCompiler, private _viewCompiler: ViewCompiler,
private _ngModuleCompiler: NgModuleCompiler, private _summaryResolver: SummaryResolver<Type>,
private _reflector: CompileReflector, private _jitEvaluator: JitEvaluator,
private _compilerConfig: CompilerConfig, private _console: Console,
private getExtraNgModuleProviders: (ngModule: any) => CompileProviderMetadata[]) {}
compileModuleAsync(moduleType: Type): Promise<object> {
// 註釋:其實 JTI 編譯在這步作的,異步編譯模塊和組件
return Promise.resolve(this._compileModuleAndComponents(moduleType, false));
}
// 註釋:作了三件事:
// 1. 加載模塊 `this._loadModules`
// 2. 編譯入口組件 `this._compileComponents`
// 3. 編譯模塊 `this._compileModule`
private _compileModuleAndComponents(moduleType: Type, isSync: boolean): SyncAsync<object> {
// 註釋:其實調用的是這步,編譯主模塊和組件
return SyncAsync.then(this._loadModules(moduleType, isSync), () => { // 註釋:先加載模塊
this._compileComponents(moduleType, null); // 註釋:異步有結果以後的回調函數,編譯主模塊上的全部入口組件
return this._compileModule(moduleType); // 註釋:返回編譯後的模塊工廠
});
}
}
複製代碼
compileModuleAsync
調用了 _compileModuleAndComponents
,並返回一個 Promise
。
這裏邏輯比較複雜,大概講下,具體的在後面 angular模塊 的時候再詳細講解,很好理解:
_compileModuleAndComponents
先調用了 this._loadModules
,異步加載解析主模塊,也就是 bootstrapModule
的 ngModule
_compileComponents
編譯主模塊上的全部組件,並經過 _compileTemplate
編譯模板(這步先跳過,後面講到編譯組件的時候會講)_compileModule
返回value 是編譯過的模塊工廠的 Promise
Promise
會調用下面的異步方法 then(moduleFactory => this.bootstrapModuleFactory(moduleFactory, options))
這裏有個地方也頗有意思,官網上的模塊常見問題上有這樣的一個問題:
答案裏有一句:當三個模塊全都導入模塊'A'時,Angular 只會首次遇到時加載一次模塊'A',以後就不會這麼作了,以前一直不知道爲何,此次看到了這樣的一段代碼:
angular/packages/compiler/src/jit/compiler.ts
export class JitCompiler {
...
private _compileModule(moduleType: Type): object {
// 註釋:從緩存中得到編譯過的模塊
let ngModuleFactory = this._compiledNgModuleCache.get(moduleType) !;
if (!ngModuleFactory) {
const moduleMeta = this._metadataResolver.getNgModuleMetadata(moduleType) !;
// Always provide a bound Compiler
const extraProviders = this.getExtraNgModuleProviders(moduleMeta.type.reference);
const outputCtx = createOutputContext();
const compileResult = this._ngModuleCompiler.compile(outputCtx, moduleMeta, extraProviders);
ngModuleFactory = this._interpretOrJit(
ngModuleJitUrl(moduleMeta), outputCtx.statements)[compileResult.ngModuleFactoryVar];
this._compiledNgModuleCache.set(moduleMeta.type.reference, ngModuleFactory);
}
return ngModuleFactory;
}
...
}
複製代碼
angular 會用 Map
緩存模塊,而且在須要返回編譯的模塊工廠時,優先去緩存中尋找已經被編譯過的模塊
angular/packages/core/src/application_ref.ts
@Injectable()
export class PlatformRef {
...
bootstrapModule<M>(
moduleType: Type<M>, compilerOptions: (CompilerOptions&BootstrapOptions)|
Array<CompilerOptions&BootstrapOptions> = []): Promise<NgModuleRef<M>> {
// 註釋:bootstrapModule` 首先經過 `optionsReducer` 遞歸 reduce 將編譯器選項 `compilerOptions` 拍平爲對象
const options = optionsReducer({}, compilerOptions);
// 註釋:這裏獲取到編譯後的模塊工廠,而後返回給 bootstrapModuleFactory建立模塊
return compileNgModuleFactory(this.injector, options, moduleType)
.then(moduleFactory => this.bootstrapModuleFactory(moduleFactory, options));
}
...
}
複製代碼
回一下上面,bootstrapModule
方法調用了 compileNgModuleFactory
返回一個 value 是 ngModuleFactory
模塊工廠的 Promise
,
接下來在 Promise
的 then
方法裏調用了 bootstrapModuleFactory
。
angular/packages/core/src/application_ref.ts
@Injectable()
export class PlatformRef {
...
bootstrapModuleFactory<M>(moduleFactory: NgModuleFactory<M>, options?: BootstrapOptions):
Promise<NgModuleRef<M>> {
// Note: We need to create the NgZone _before_ we instantiate the module,
// as instantiating the module creates some providers eagerly.
// So we create a mini parent injector that just contains the new NgZone and
// pass that as parent to the NgModuleFactory.
const ngZoneOption = options ? options.ngZone : undefined;
const ngZone = getNgZone(ngZoneOption);
const providers: StaticProvider[] = [{provide: NgZone, useValue: ngZone}];
// Attention: Don't use ApplicationRef.run here,
// as we want to be sure that all possible constructor calls are inside `ngZone.run`!
return ngZone.run(() => {
const ngZoneInjector = Injector.create(
{providers: providers, parent: this.injector, name: moduleFactory.moduleType.name});
const moduleRef = <InternalNgModuleRef<M>>moduleFactory.create(ngZoneInjector);
const exceptionHandler: ErrorHandler = moduleRef.injector.get(ErrorHandler, null);
if (!exceptionHandler) {
throw new Error('No ErrorHandler. Is platform module (BrowserModule) included?');
}
moduleRef.onDestroy(() => remove(this._modules, moduleRef));
ngZone !.runOutsideAngular(
() => ngZone !.onError.subscribe(
{next: (error: any) => { exceptionHandler.handleError(error); }}));
return _callAndReportToErrorHandler(exceptionHandler, ngZone !, () => {
const initStatus: ApplicationInitStatus = moduleRef.injector.get(ApplicationInitStatus);
initStatus.runInitializers();
return initStatus.donePromise.then(() => {
this._moduleDoBootstrap(moduleRef);
return moduleRef;
});
});
});
}
...
}
複製代碼
這裏作的事情也很少:
NgZone
(NgZone
放到下一節講):angular/packages/core/src/application_ref.ts
function getNgZone(ngZoneOption?: NgZone | 'zone.js' | 'noop'): NgZone {
let ngZone: NgZone;
if (ngZoneOption === 'noop') {
ngZone = new NoopNgZone();
} else {
ngZone = (ngZoneOption === 'zone.js' ? undefined : ngZoneOption) ||
new NgZone({enableLongStackTrace: isDevMode()});
}
return ngZone;
}
複製代碼
providers
,做爲根模塊的父注入器angular/packages/core/src/application_ref.ts
// 註釋:會被 onInvoke 執行
const ngZoneInjector = Injector.create(
{providers: providers, parent: this.injector, name: moduleFactory.moduleType.name});
const moduleRef = <InternalNgModuleRef<M>>moduleFactory.create(ngZoneInjector);
複製代碼
ngZone.run
,啓動 ngZone
並讓全部的 angular 程序跑在這個 zone
上下文環境裏ngZone.run
啓動 zone 以後,建立一個初始的注入器,並使用該注入器做爲根模塊的父注入器建立根模塊實例這裏面內容很少,用人話總結下:
bootstrapModule
會先合併配置並調用編譯模塊的工廠函數 compileNgModuleFactory
開始編譯模塊compileNgModuleFactory
經過平臺實例 PlatformRef
的注射器 injector
獲取 JIT編譯器工廠 JitCompilerFactory
,JIT 編譯器工廠 JitCompilerFactory
又經過 createCompiler
方法,建立編譯器 Compiler
實例 CompilerImpl
,並開始編譯根模塊和全部的組件,CompilerImpl
調用 JitCompiler
JIT 編譯實例 最後實際上編譯是JitCompiler
去編譯的JitCompiler
加載模塊 => 編譯組件 => 編譯模塊NgModuleFactory
的 Promise
Promise.then()
裏調用 bootstrapModuleFactory
bootstrapModuleFactory
建立 NgZone 實例並開始運行 zone ,讓全部的 angular 程序跑在這個 zone
上下文環境裏injector
並實例化模塊工廠建立模塊實例 NgModuleRef