试读angular源码第二章:引导模块bootstrapModule
栏目: JavaScript · 发布时间: 5年前
内容简介:今天 angularv8的正式版发了,但是除了动态路由那里没觉得有啥大变化,有点小失望
今天 angularv8的正式版发了,但是除了动态路由那里没觉得有啥大变化,有点小失望
引导模块
Angular 应用是模块化的,它拥有自己的模块化系统,称作 NgModule
。
一个 NgModule
就是一个容器,用于存放一些内聚的代码块,这些代码块专注于某个应用领域、某个工作流或一组紧密相关的功能。
它可以包含一些组件、服务提供商或其它代码文件,其作用域由包含它们的 NgModule
定义。 它还可以导入一些由其它模块中导出的功能,并导出一些指定的功能供其它 NgModule
使用。
每个 Angular 应用都至少有一个 NgModule
类,也就是根模块,它习惯上命名为 AppModule
,并位于一个名叫 app.module.ts
的文件中。
引导这个根模块就可以启动你的应用。
当 bootstrap(引导)根模块之后, NgModule
会继而实例化元数据中 bootstrap
。
bootstrap 应用的主视图,称为根组件。它是应用中所有其它视图的宿主。只有根模块才应该设置这个 bootstrap
属性
bootstrapModule
bootstrapModule
是在上一节 platformBrowserDynamic()
返回的平台实例 PlatformRef
中的一个方法,用于引导启动实例根模块。
angular/packages/core/src/application_ref.ts
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
接受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
,编译器选项和要引导实例化的根模块。
compileNgModuleFactory
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); } 复制代码
compileNgModuleFactory
在这里其实就是 compileNgModuleFactory__PRE_R3__
-
在这里,先
通过平台实例
PlatformRef
的注射器injector
获取了编译器实例,其实也就是coreDynamic
提供的JitCompilerFactory
-
然后调用 JIT 编译器工厂
JitCompilerFactory
的createCompiler
方法,创建编译器Compiler
实例CompilerImpl
-
最后通过编译器
Compiler
实例CompilerImpl
异步编译给定的NgModule
及其所有组件
coreDynamic
提供的 JitCompilerFactory
调用 createCompiler
创建编译器实例的时候,其实是在这里注入了服务供应商 CompilerImpl
,
所以最后创建了的编译器实例 Compiler 其实是 CompilerImpl。
angular/packages/platform-browser-dynamic/src/compiler_factory.ts
{ provide: Compiler, useClass: CompilerImpl, deps: [Injector, CompileMetadataResolver....]} 复制代码
CompilerImpl
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) { 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> { return Promise.resolve(this._compileModuleAndComponents(moduleType, false)); // 注释:其实 JTI 编译在这步做的 } 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
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
缓存模块,并且在需要返回编译的模块工厂时,优先去缓存中寻找已经被编译过的模块
bootstrapModuleFactory
angular/packages/core/src/application_ref.ts
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
返回一个 value 是 ngModuleFactory
模块工厂的 Promise
,
接下来在 Promise
的 then
方法里调用了 bootstrapModuleFactory
。
angular/packages/core/src/application_ref.ts
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; } 复制代码
-
接下来 angualr 创建了一个包含 ngZone 的
providers
,作为根模块的父注入器
angular/packages/core/src/application_ref.ts
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
去编译的 -
异步编译根模块和所有的组件
, 并放入缓存中
,最后返回 value 是模块工厂
NgModuleFactory
的Promise
-
然后在
Promise.then()
里调用bootstrapModuleFactory
-
bootstrapModuleFactory
创建 NgZone 实例并开始运行 zone , 让所有的 angular 程序跑在这个zone
上下文环境里 -
开始运行 zone ,
创建根模块的父注入器
injector
并实例化模块工厂创建模块实例NgModuleRef
以上所述就是小编给大家介绍的《试读angular源码第二章:引导模块bootstrapModule》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- 试读angular源码第一章:开场与platformBrowserDynamic
- 试读angular源码第四章:angular模块及JIT编译模块
- 【源码阅读】AndPermission源码阅读
- ReactNative源码解析-初识源码
- 【源码阅读】Gson源码阅读
- Spring源码系列:BeanDefinition源码解析
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
JavaScript编程精解
Marijn Haverbeke / 徐涛 / 机械工业出版社华章公司 / 2012-10-1 / 49.00元
如果你只想阅读一本关于JavaScript的图书,那么本书应该是你的首选。本书由世界级JavaScript程序员撰写,JavaScript之父和多位JavaScript专家鼎力推荐。本书适合作为系统学习JavaScript的参考书,它在写作思路上几乎与现有的所有同类书都不同,打破常规,将编程原理与运用规则完美地结合在一起,而且将所有知识点与一个又一个经典的编程故事融合在一起,读者可以在轻松的游戏式......一起来看看 《JavaScript编程精解》 这本书的介绍吧!