直接看人話總結html
承接上一章git
該文章項目地址github
文章地址typescript
歡迎看看個人類angular框架bootstrap
試讀angular源碼第一章:開場與platformBrowserDynamic數組
試讀angular源碼第二章:引導模塊bootstrapModulepromise
試讀angular源碼第四章:angular模塊及JIT編譯模塊緩存
NgModule
是一個帶有 @NgModule
裝飾器的類。
@NgModule
的參數是一個元數據對象,用於描述如何編譯組件的模板,以及如何在運行時建立注入器。
它會標出該模塊本身的組件、指令和管道,經過 exports
屬性公開其中的一部分,以便外部組件使用它們。
NgModule
還能把一些服務提供商添加到應用的依賴注入器中。
在以前的例子中,咱們經過 platformBrowserDynamic().bootstrapModule(AppModule).catch(err => console.error(err));
引導初始化時,bootstrapModule
方法傳入的第一個參數就是angular 模塊 NgModule
。
這裏咱們要先講2個概念:JIT 和 AOT
Angular 提供了兩種方式來編譯你的應用:
JIT的流程
AOT的流程
.metadata.json
文件但這裏咱們只討論 JIT 模式!
angular/packages/core/src/metadata/ng_module.ts
export interface NgModuleDecorator {
(obj?: NgModule): TypeDecorator;
new (obj?: NgModule): NgModule;
}
export interface NgModule {
providers?: Provider[];
declarations?: Array<Type<any>|any[]>;
imports?: Array<Type<any>|ModuleWithProviders<{}>|any[]>;
exports?: Array<Type<any>|any[]>;
entryComponents?: Array<Type<any>|any[]>;
bootstrap?: Array<Type<any>|any[]>;
schemas?: Array<SchemaMetadata|any[]>;
id?: string;
jit?: true;
}
/** * @Annotation * @publicApi */
export const NgModule: NgModuleDecorator = makeDecorator(
'NgModule', (ngModule: NgModule) => ngModule, undefined, undefined,
/** * Decorator that marks the following class as an NgModule, and supplies * configuration metadata for it. * * * The `declarations` and `entryComponents` options configure the compiler * with information about what belongs to the NgModule. * * The `providers` options configures the NgModule's injector to provide * dependencies the NgModule members. * * The `imports` and `exports` options bring in members from other modules, and make * this module's members available to others. */
(type: NgModuleType, meta: NgModule) => SWITCH_COMPILE_NGMODULE(type, meta));
複製代碼
裝飾器 @NgModule
的做用是描述 angular 模塊,並提供 元數據 支持
例如幾個經常使用的元數據:
providers?: Provider[];
依賴注入系統提供可注入項的重點
providers
能夠提供給全局任何指令管道服務,至關於 @Injectable
爲 root
declarations
屬於此模塊的組件,指令和管道的集合imports
引入其餘模塊的 export
exports
處處給其餘模塊的 imports
bootstrap
引導用的入口組件,一般根模塊和路由懶加載須要設置entryComponents
入口組件 不經常使用,angular 編譯器會自動將 bootstrap
編譯到裏面而 @NgModule
由 makeDecorator
構造而來:
makeDecorator
用來建立 angular 裝飾器,像 @NgModule
@Component
@Pipe
@Directive
都用改方法建立:
angular/packages/core/src/util/decorators.ts
export const ANNOTATIONS = '__annotations__';
export function makeDecorator<T>(
name: string,
props?: (...args: any[]) => any, // 註釋:args 就是裝飾器的參數用來處理裝飾器參數
parentClass?: any,
additionalProcessing?: (type: Type<T>) => void, // 註釋:額外的處理
typeFn?: (type: Type<T>, ...args: any[]) => void) // 註釋:用來處理class的原型
: {new (...args: any[]): any; (...args: any[]): any; (...args: any[]): (cls: any) => any;} {
const metaCtor = makeMetadataCtor(props); // 註釋:建立 Metadata 的構造函數
function DecoratorFactory(...args: any[]): (cls: Type<T>) => any {
if (this instanceof DecoratorFactory) { // 註釋:經過 args 用來設置默認值
metaCtor.call(this, ...args); // 註釋:this就是DecoratorFactory工廠,也就是參數對象
return this;
}
const annotationInstance = new (DecoratorFactory as any)(...args); // 註釋:註解實例實際上就是裝飾器的參數對象
return function TypeDecorator(cls: Type<T>) { // 註釋:cls就是裝飾器裝飾的類構造函數
if (typeFn) typeFn(cls, ...args);
// Use of Object.defineProperty is important since it creates non-enumerable property which
// prevents the property is copied during subclassing.
const annotations = cls.hasOwnProperty(ANNOTATIONS) ?
(cls as any)[ANNOTATIONS] :
Object.defineProperty(cls, ANNOTATIONS, {value: []})[ANNOTATIONS];
annotations.push(annotationInstance); // 註釋:將裝飾器的處理結果存在
if (additionalProcessing) additionalProcessing(cls);
return cls;
};
}
if (parentClass) {
DecoratorFactory.prototype = Object.create(parentClass.prototype); // 註釋:使實例 DecoratorFactory 繼承繼承 parentClass
}
DecoratorFactory.prototype.ngMetadataName = name; // 註釋:裝飾器名稱會被放在原型屬性 ngMetadataName 上
(DecoratorFactory as any).annotationCls = DecoratorFactory;
return DecoratorFactory as any;
}
function makeMetadataCtor(props?: (...args: any[]) => any): any {
return function ctor(...args: any[]) {
if (props) {
const values = props(...args);
for (const propName in values) {
this[propName] = values[propName];
}
}
};
}
複製代碼
參數:
name: string
就是裝飾器的名稱props?: (...args: any[]) => any
args
就是裝飾器的參數,props
用來處理裝飾器參數,可用於默認值設置parentClass?: any
父類,提供給 DecoratorFactory
實例用來繼承additionalProcessing?: (type: Type<T>) => void
對類構造函數進行額外處理,參數是裝飾器的宿主類的構造函數typeFn?: (type: Type<T>, ...args: any[]) => void)
在裝飾器的返回函數中,會再次執行下回調函數,參數是類構造函數和參數在這裏 makeDecorator
基本上作了這幾個事情:
makeMetadataCtor
建立一個給類構造函數附加初始值的函數 ,本質上是建立 Metadata 的構造函數this
是註解工廠 DecoratorFactory
的實例,則經過上面給類構造函數附加初始值的函數,傳入 this
和裝飾器參數 args
typeFn
傳入類構造函數和參數,修改類構造函數DecoratorFactory
的實例 ,註解工廠方法會遞歸執行,直到 this
是註解工廠 DecoratorFactory
的實例 (註解工廠 DecoratorFactory
的實例實際上就是裝飾器的參數對象)__annotations__:any[]
屬性,把裝飾器處理結果(註解實例===參數對象)保存在類構造函數的 __annotations__:any[]
屬性數組中,並提供給編譯器 compiler
使用DecoratorFactory
的原型,繼承父類 parentClass
並添加元數據的名字 ngMetadataName
注意這裏:DecoratorFactory.prototype = Object.create(parentClass.prototype);
經過使用 Object.create
避免執行一次 parentClass
來繼承父類
makeDecorator
的做用就是構造返回一個函數 DecoratorFactory
用做 裝飾器,並建立裝飾器工廠 DecoratorFactory
實例@NgModule
就是一個接受參數並返回函數的方法,裝飾器會把 @NgModule
傳入的元數據對象進行處理並生成註解工廠 DecoratorFactory
的實例掛在到 __annotations__:any
提供給編譯器使用。劃重點:AppModule.__annotations__
:
最後你們能夠打印下 (AppModule as any).__annotations__
來進行驗證,這就是存在模塊類上的註解實例。
此處建議結合第二章bootstrapModule一塊兒閱讀
先看下以前構建的 JitCompilerFactory
時注入過的服務,這些在後面編譯的時候會大量用到:
angular/packages/platform-browser-dynamic/src/compiler_factory.ts
/** * A set of providers that provide `JitCompiler` and its dependencies to use for * template compilation. */
export const COMPILER_PROVIDERS = <StaticProvider[]>[
// 註釋:這裏也是一個核心點-編譯反射器
{provide: CompileReflector, useValue: new JitReflector()},
// 註釋:ResourceLoader- 資源加載器
{provide: ResourceLoader, useValue: _NO_RESOURCE_LOADER},
// 註釋:jit 摘要解析器
{provide: JitSummaryResolver, deps: []},
// 註釋:摘要解析器
{provide: SummaryResolver, useExisting: JitSummaryResolver},
{provide: Console, deps: []},
// 註釋:語法分析器
{provide: Lexer, deps: []},
// 註釋:解析器器
{provide: Parser, deps: [Lexer]},
// 註釋:基本的HTML解析器
{
provide: baseHtmlParser,
useClass: HtmlParser,
deps: [],
},
// 註釋:國際化的HTML解析器
{
provide: I18NHtmlParser,
useFactory: (parser: HtmlParser, translations: string | null, format: string,
config: CompilerConfig, console: Console) => {
translations = translations || '';
const missingTranslation =
translations ? config.missingTranslation ! : MissingTranslationStrategy.Ignore;
// 註釋:new 國際化的HTML解析器
return new I18NHtmlParser(parser, translations, format, missingTranslation, console);
},
deps: [
baseHtmlParser,
[new Optional(), new Inject(TRANSLATIONS)],
[new Optional(), new Inject(TRANSLATIONS_FORMAT)],
[CompilerConfig],
[Console],
]
},
{
provide: HtmlParser,
useExisting: I18NHtmlParser,
},
// 註釋:模板解析器
{
provide: TemplateParser, deps: [CompilerConfig, CompileReflector,
Parser, ElementSchemaRegistry,
I18NHtmlParser, Console]
},
{ provide: JitEvaluator, useClass: JitEvaluator, deps: [] },
// 註釋:指令規範器
{ provide: DirectiveNormalizer, deps: [ResourceLoader, UrlResolver, HtmlParser, CompilerConfig]},
// 註釋:元數據解析器
{ provide: CompileMetadataResolver, deps: [CompilerConfig, HtmlParser, NgModuleResolver,
DirectiveResolver, PipeResolver,
SummaryResolver,
ElementSchemaRegistry,
DirectiveNormalizer, Console,
[Optional, StaticSymbolCache],
CompileReflector,
[Optional, ERROR_COLLECTOR_TOKEN]]},
DEFAULT_PACKAGE_URL_PROVIDER,
// 註釋:樣式編譯器
{ provide: StyleCompiler, deps: [UrlResolver]},
// 註釋:view 編譯器
{ provide: ViewCompiler, deps: [CompileReflector]},
// 註釋:NgModule編譯器
{ provide: NgModuleCompiler, deps: [CompileReflector] },
// 註釋:編譯器配置項目
{ provide: CompilerConfig, useValue: new CompilerConfig()},
// 註釋:JIT時,Compiler的服務供應商 CompilerImpl
{ provide: Compiler, useClass: CompilerImpl, deps: [Injector, CompileMetadataResolver,
TemplateParser, StyleCompiler,
ViewCompiler, NgModuleCompiler,
SummaryResolver, CompileReflector, JitEvaluator, CompilerConfig,
Console]},
// 註釋:DOM schema
{ provide: DomElementSchemaRegistry, deps: []},
// 註釋:Element schema
{ provide: ElementSchemaRegistry, useExisting: DomElementSchemaRegistry},
// 註釋:URL解析器
{ provide: UrlResolver, deps: [PACKAGE_ROOT_URL]},
// 註釋:指令解析器
{ provide: DirectiveResolver, deps: [CompileReflector]},
// 註釋:管道解析器
{ provide: PipeResolver, deps: [CompileReflector]},
// 註釋:模塊解析器
{ provide: NgModuleResolver, deps: [CompileReflector]},
];
複製代碼
講完了 @NgModule
,回到以前的文章,看下 bootstrapModule
這個方法如何編譯模塊:
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>> {
const compilerFactory: CompilerFactory = injector.get(CompilerFactory);
const compiler = compilerFactory.createCompiler([options]);
return compiler.compileModuleAsync(moduleType);
}
@Injectable()
export class PlatformRef {
...
bootstrapModule<M>(moduleType: Type<M>, compilerOptions: (CompilerOptions&BootstrapOptions)| Array<CompilerOptions&BootstrapOptions> = []):Promise<NgModuleRef<M>> {
const options = optionsReducer({}, compilerOptions);
return compileNgModuleFactory(this.injector, options, moduleType)
.then(moduleFactory => this.bootstrapModuleFactory(moduleFactory, options));
}
...
}
複製代碼
bootstrapModule
調用了 compileNgModuleFactory
這個方法,而最後其實在 JIT 模式下,其實是 coreDynamic
提供的 JitCompilerFactory
建立了 CompilerImpl
實例並建立了代理 JitCompiler
去實現真正的編譯。
angular/packages/platform-browser-dynamic/src/compiler_factory.ts
export class CompilerImpl implements Compiler {
...
private _delegate: JitCompiler;
...
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));
}
compileModuleAsync<T>(moduleType: Type<T>): Promise<NgModuleFactory<T>> { // 註釋:異步建立模塊及其子組件
return this._delegate.compileModuleAsync(moduleType) as Promise<NgModuleFactory<T>>;
}
...
}
複製代碼
最終由 JitCompiler
執行 compileModuleAsync
方法編譯模塊
編譯模塊和組件的實際工做是由 CompilerImpl
交由代理 JitCompiler
的方法 compileModuleAsync<T>(moduleType: Type<T>)
完成的:
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
在這裏只作了三件事:
this._loadModules
this._compileComponents
this._compileModule
angular/packages/compiler/src/jit/compiler.ts
export class JitCompiler {
...
// 註釋:異步加載解析主模塊,也就是 bootstrap 的 ngModule
private _loadModules(mainModule: any, isSync: boolean): SyncAsync<any> {
const loading: Promise<any>[] = [];
// 註釋:從元數據中得到根模塊的 __annotations__ 並格式化
const mainNgModule = this._metadataResolver.getNgModuleMetadata(mainModule) !;
// 註釋:過濾 AOT 模塊並異步編加載數據中所有指令組件和和管道
// 註釋:過濾掉根模塊元數據中的 AOT 模塊
this._filterJitIdentifiers(mainNgModule.transitiveModule.modules).forEach((nestedNgModule) => {
// getNgModuleMetadata only returns null if the value passed in is not an NgModule
const moduleMeta = this._metadataResolver.getNgModuleMetadata(nestedNgModule) !;
this._filterJitIdentifiers(moduleMeta.declaredDirectives).forEach((ref) => {
// 註釋:異步編加載數據中所有指令組件和和管道
const promise =
this._metadataResolver.loadDirectiveMetadata(moduleMeta.type.reference, ref, isSync);
if (promise) {
loading.push(promise);
}
});
this._filterJitIdentifiers(moduleMeta.declaredPipes)
.forEach((ref) => this._metadataResolver.getOrLoadPipeMetadata(ref));
});
// 註釋:最後所有並行 Promise
return SyncAsync.all(loading);
}
...
// 註釋:過濾掉根模塊元數據中的 AOT 模塊
hasAotSummary(ref: Type) { return !!this._summaryResolver.resolveSummary(ref); }
// 註釋:過濾掉根模塊元數據中的 AOT 模塊
private _filterJitIdentifiers(ids: CompileIdentifierMetadata[]): any[] {
return ids.map(mod => mod.reference).filter((ref) => !this.hasAotSummary(ref));
}
}
複製代碼
_loadModules
接受2個參數:
mainModule: any
模塊類isSync: boolean
是不是同步加載 在 bootstrapModule
的時候是 false
,異步加載_loadModules
作了什麼?
this._metadataResolver.getNgModuleMetadata
獲取到以前 makeDecorator
在模塊類上建立的靜態屬性 __annotations__
並編譯模塊的元數據this._filterJitIdentifiers
遞歸過濾掉 AOT 模塊this._metadataResolver.loadDirectiveMetadata(moduleMeta.type.reference, ref, isSync)
異步加載所有指令組件和和管道的元數據Promise
並返回異步編譯的結果AppModule
關聯的模塊的元數據都已經加載進了緩存中,包括了從 AppModule
開始除了懶加載模塊以外的的整個模塊樹,樹上的全部指令,組件和管道,以及全部的服務。接下來繼續看 _compileModuleAndComponents
在加載完模塊以後,調用了 this._compileComponents
編譯組件:
angular/packages/compiler/src/jit/compiler.ts
export class JitCompiler {
// 註釋:作了三件事:
// 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); // 註釋:返回編譯後的模塊工廠
});
}
}
複製代碼
_compileComponents
方法用來編譯根模塊組件的模板:
angular/packages/compiler/src/jit/compiler.ts
export class JitCompiler {
// 註釋:編譯主模塊上的全部組件
// 主要目的:拿到被聲明的組件的模板、入口組件的模板,最終拿到了全部涉及的模板,放在 templates 中
_compileComponents(mainModule: Type, allComponentFactories: object[]|null) {
// 註釋:獲取主模塊
const ngModule = this._metadataResolver.getNgModuleMetadata(mainModule) !;
const moduleByJitDirective = new Map<any, CompileNgModuleMetadata>();
const templates = new Set<CompiledTemplate>();
// 註釋:過濾AOT模塊
const transJitModules = this._filterJitIdentifiers(ngModule.transitiveModule.modules);
// 註釋:編譯各個模塊的模板,(localMod 是模塊的class)
transJitModules.forEach((localMod) => {
const localModuleMeta = this._metadataResolver.getNgModuleMetadata(localMod) !;
// 註釋:指令和組件都是 declaredDirectives (在angular裏 @Component組件 繼承了 指令@Directive)
this._filterJitIdentifiers(localModuleMeta.declaredDirectives).forEach((dirRef) => {
moduleByJitDirective.set(dirRef, localModuleMeta);
const dirMeta = this._metadataResolver.getDirectiveMetadata(dirRef);
// 註釋:只編譯組件
// 註釋:拿到全部的模板,並放在 templates:Set 中
if (dirMeta.isComponent) {
templates.add(this._createCompiledTemplate(dirMeta, localModuleMeta));
if (allComponentFactories) {
const template =
this._createCompiledHostTemplate(dirMeta.type.reference, localModuleMeta);
templates.add(template);
allComponentFactories.push(dirMeta.componentFactory as object);
}
}
});
});
// 註釋:編譯入口組件的模板
transJitModules.forEach((localMod) => {
const localModuleMeta = this._metadataResolver.getNgModuleMetadata(localMod) !;
this._filterJitIdentifiers(localModuleMeta.declaredDirectives).forEach((dirRef) => {
const dirMeta = this._metadataResolver.getDirectiveMetadata(dirRef);
if (dirMeta.isComponent) {
dirMeta.entryComponents.forEach((entryComponentType) => {
const moduleMeta = moduleByJitDirective.get(entryComponentType.componentType) !;
templates.add(
this._createCompiledHostTemplate(entryComponentType.componentType, moduleMeta));
});
}
});
localModuleMeta.entryComponents.forEach((entryComponentType) => {
if (!this.hasAotSummary(entryComponentType.componentType)) {
const moduleMeta = moduleByJitDirective.get(entryComponentType.componentType) !;
templates.add(
this._createCompiledHostTemplate(entryComponentType.componentType, moduleMeta));
}
});
});
// 註釋:執行 _compileTemplate 編譯模板
templates.forEach((template) => this._compileTemplate(template));
}
}
複製代碼
在這裏主要作了下面這幾件事:
this._metadataResolver.getNgModuleMetadata
像以前編譯模板同樣獲取根模塊this._filterJitIdentifiers
過濾 AOT 模塊declarations
),並編譯其模板entryComponents
),並編譯其模板至於如何編譯的模板,以後講組件的時候再說吧。
_compileComponents
的目的是拿到被聲明的組件的模板、入口組件的模板,最終拿到了全部涉及的模板
angular/packages/compiler/src/jit/compiler.ts
export class JitCompiler {
...
// 註釋:angular 會用 Map 緩存模塊工廠,而且在須要返回編譯的模塊工廠時,優先去緩存中尋找已經被編譯過的模塊工廠
private _compileModule(moduleType: Type): object {
// 註釋:從緩存拿到模塊工廠
let ngModuleFactory = this._compiledNgModuleCache.get(moduleType) !; // 註釋:讀取緩存
if (!ngModuleFactory) {
// 註釋:讀取模塊的元數據
const moduleMeta = this._metadataResolver.getNgModuleMetadata(moduleType) !;
// 註釋:調用實例化 JITCompiler 時候傳入方法,建立額外的模塊服務供應商 (在 CompilerImpl 傳入)
// Always provide a bound Compiler
const extraProviders = this.getExtraNgModuleProviders(moduleMeta.type.reference);
// 註釋:建立輸出上下
const outputCtx = createOutputContext();
// 註釋:構建編譯結果:是一個對象,只有 ngModuleFactoryVar 這麼一個屬性:ngModuleFactoryVar: "AppModuleNgFactory",內部經過構建服務供應商和模塊的AST,很複雜
const compileResult = this._ngModuleCompiler.compile(outputCtx, moduleMeta, extraProviders);
console.log(77777, moduleType, compileResult);
// 註釋:動態建立出一個模塊的工廠方法
ngModuleFactory = this._interpretOrJit(
ngModuleJitUrl(moduleMeta), outputCtx.statements)[compileResult.ngModuleFactoryVar];
this._compiledNgModuleCache.set(moduleMeta.type.reference, ngModuleFactory);
}
return ngModuleFactory;
}
...
}
複製代碼
這裏也很簡單:
JITCompiler
時候傳入方法,建立額外的模塊服務供應商 (在 CompilerImpl
傳入)ngModuleFactoryVar
這麼一個屬性,估計是把編譯結果放緩存了:ngModuleFactoryVar: "AppModuleNgFactory"
模塊編譯器這裏比較複雜:
angular/packages/compiler/src/ng_module_compiler.ts
export class NgModuleCompiler {
constructor(private reflector: CompileReflector) {}
compile(
ctx: OutputContext, ngModuleMeta: CompileNgModuleMetadata,
extraProviders: CompileProviderMetadata[]): NgModuleCompileResult {
// 註釋:生成一個關於模塊類及文件位置的對象
const sourceSpan = typeSourceSpan('NgModule', ngModuleMeta.type);
// 註釋:得到入口組件的工廠函數,默認就有 <ng-component/> 和 <app-root/>
const entryComponentFactories = ngModuleMeta.transitiveModule.entryComponents;
const bootstrapComponents = ngModuleMeta.bootstrapComponents;
// 註釋:分析模塊及模塊引入的模塊的服務供應商
const providerParser =
new NgModuleProviderAnalyzer(this.reflector, ngModuleMeta, extraProviders, sourceSpan);
// 註釋:這塊是AST了,生成了模塊中全部服務供應商的函數 AST
const providerDefs =
[componentFactoryResolverProviderDef(
this.reflector, ctx, NodeFlags.None, entryComponentFactories)]
.concat(providerParser.parse().map((provider) => providerDef(ctx, provider)))
.map(({providerExpr, depsExpr, flags, tokenExpr}) => {
return o.importExpr(Identifiers.moduleProviderDef).callFn([
o.literal(flags), tokenExpr, providerExpr, depsExpr
]);
});
// 註釋:這塊是AST了,生成了模塊的 AST
const ngModuleDef = o.importExpr(Identifiers.moduleDef).callFn([o.literalArr(providerDefs)]);
const ngModuleDefFactory = o.fn(
[new o.FnParam(LOG_VAR.name !)], [new o.ReturnStatement(ngModuleDef)], o.INFERRED_TYPE);
// 註釋:建立一個字符串
const ngModuleFactoryVar = `${identifierName(ngModuleMeta.type)}NgFactory`;
// 註釋:保存在上下文中聲明中
this._createNgModuleFactory(
ctx, ngModuleMeta.type.reference, o.importExpr(Identifiers.createModuleFactory).callFn([
ctx.importExpr(ngModuleMeta.type.reference),
o.literalArr(bootstrapComponents.map(id => ctx.importExpr(id.reference))),
ngModuleDefFactory
]));
if (ngModuleMeta.id) {
const id = typeof ngModuleMeta.id === 'string' ? o.literal(ngModuleMeta.id) :
ctx.importExpr(ngModuleMeta.id);
const registerFactoryStmt = o.importExpr(Identifiers.RegisterModuleFactoryFn)
.callFn([id, o.variable(ngModuleFactoryVar)])
.toStmt();
// 註釋:保存在上下文中
ctx.statements.push(registerFactoryStmt);
}
// 註釋:返回編譯結果
return new NgModuleCompileResult(ngModuleFactoryVar);
}
...
}
複製代碼
這裏作了下面幾件事情:
<ng-component/>
和 <app-root/>
其實我沒太看懂爲何要轉換爲 AST,這裏面留幾個坑
總結下 @NgModule
大概發生了什麼
makeDecorator
生成 @NgModule
註解@NgModule
經過傳入的參數和反射,生成註解附加在模塊類的靜態屬性 __annotations__
並提供給 JitCompiler
編譯器使用bootstrapModule
被調用時候,在 JIT 模式下建立了代理 JitCompiler
去實現真正的編譯JitCompiler
編譯模塊調用了 compileModuleAsync
並返回模塊工廠,而且只作了三件事:
this._loadModules
this._compileComponents
this._compileModule