深度刨析@angular/platform-browser-dynamic

1. 前言

作为 angular 应用的入口模块,了解他的重要性是显而易见的!

2. angular 之 main.ts

main.ts 是这个样子的,下面我就去解开 platformBrowserDynamic 神秘面纱!

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

3. 解析 platformBrowserDynamic

platformBrowserDynamic 源码位于platform-browser-dynamic.ts

首先我们来看一下接口定义

1
2
3
4
5
6
/**
* @publicApi
*/
export declare const platformBrowserDynamic: (
extraProviders?: StaticProvider[] | undefined
) => PlatformRef;

方法返回的是一个 PlatformRef, 可以接受一个或多个额外的 Providers.

先看一下返回值对象 PlatformRef, 这是 PlatformRef 的源码及介绍

Angular platform 是 Angular 在网页上的入口点。
每一 web 页面有且仅有一个 platform。所有的服务(如反射)都绑定在其 scope 内并共享给整个 Angular application。
当使用 Angular platform 创建平台时,页面的 platform 会隐式初始化
factory,例如’PlatformBrowser’,或者通过调用’createPlatform()’函数显式调用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
/**
* The Angular platform is the entry point for Angular on a web page.
* Each page has exactly one platform. Services (such as reflection) which are common
* to every Angular application running on the page are bound in its scope.
* A page's platform is initialized implicitly when a platform is created using a platform
* factory such as `PlatformBrowser`, or explicitly by calling the `createPlatform()` function.
*
* @publicApi
*/
export declare class PlatformRef {
private _injector;
private _modules;
private _destroyListeners;
private _destroyed;
/**
* Creates an instance of an `@NgModule` for the given platform.
*
* @deprecated Passing NgModule factories as the `PlatformRef.bootstrapModuleFactory` function
* argument is deprecated. Use the `PlatformRef.bootstrapModule` API instead.
*/
bootstrapModuleFactory<M>(
moduleFactory: NgModuleFactory<M>,
options?: BootstrapOptions
): Promise<NgModuleRef<M>>;
/**
* Creates an instance of an `@NgModule` for a given platform.
*
* @usageNotes
* ### Simple Example
*
* ```typescript
* @NgModule({
* imports: [BrowserModule]
* })
* class MyModule {}
*
* let moduleRef = platformBrowser().bootstrapModule(MyModule);
* ```
*
*/
bootstrapModule<M>(
moduleType: Type<M>,
compilerOptions?:
| (CompilerOptions & BootstrapOptions)
| Array<CompilerOptions & BootstrapOptions>
): Promise<NgModuleRef<M>>;
private _moduleDoBootstrap;
/**
* Registers a listener to be called when the platform is destroyed.
*/
onDestroy(callback: () => void): void;
/**
* Retrieves the platform {@link Injector}, which is the parent injector for
* every Angular application on the page and provides singleton providers.
*/
get injector(): Injector;
/**
* Destroys the current Angular platform and all Angular applications on the page.
* Destroys all modules and listeners registered with the platform.
*/
destroy(): void;
get destroyed(): boolean;
static ɵfac: i0.ɵɵFactoryDeclaration<PlatformRef, never>;
static ɵprov: i0.ɵɵInjectableDeclaration<PlatformRef>;
}

packages\core\src\application_ref.ts

为给定平台创建“@NgModule”的实例。

1
2
3
4
5
6
7
8
9
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));
}

NgModuleFactory 的创建过程, 重点是创建 R3NgModuleFactory

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
export function compileNgModuleFactory<M>(
injector: Injector,
options: CompilerOptions,
moduleType: Type<M>
): Promise<NgModuleFactory<M>> {
ngDevMode && assertNgModuleType(moduleType);

const moduleFactory = new R3NgModuleFactory(moduleType);

// All of the logic below is irrelevant for AOT-compiled code.
if (typeof ngJitMode !== "undefined" && !ngJitMode) {
return Promise.resolve(moduleFactory);
}

const compilerOptions = injector.get(COMPILER_OPTIONS, []).concat(options);

// Configure the compiler to use the provided options. This call may fail when multiple modules
// are bootstrapped with incompatible options, as a component can only be compiled according to
// a single set of options.
setJitOptions({
defaultEncapsulation: _lastDefined(
compilerOptions.map((opts) => opts.defaultEncapsulation)
),
preserveWhitespaces: _lastDefined(
compilerOptions.map((opts) => opts.preserveWhitespaces)
),
});

if (isComponentResourceResolutionQueueEmpty()) {
return Promise.resolve(moduleFactory);
}

const compilerProviders = _mergeArrays(
compilerOptions.map((o) => o.providers!)
);

// In case there are no compiler providers, we just return the module factory as
// there won't be any resource loader. This can happen with Ivy, because AOT compiled
// modules can be still passed through "bootstrapModule". In that case we shouldn't
// unnecessarily require the JIT compiler.
if (compilerProviders.length === 0) {
return Promise.resolve(moduleFactory);
}

const compiler = getCompilerFacade({
usage: JitCompilerUsage.Decorator,
kind: "NgModule",
type: moduleType,
});
const compilerInjector = Injector.create({ providers: compilerProviders });
const resourceLoader = compilerInjector.get(compiler.ResourceLoader);
// The resource loader can also return a string while the "resolveComponentResources"
// always expects a promise. Therefore we need to wrap the returned value in a promise.
return resolveComponentResources((url) =>
Promise.resolve(resourceLoader.get(url))
).then(() => moduleFactory);
}

核心代码在 R3NgModuleFactory 的创建部分
packages\core\src\render3\ng_module_ref.ts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

constructor(ngModuleType: Type<T>, public _parent: Injector|null) {
super();
const ngModuleDef = getNgModuleDef(ngModuleType);
ngDevMode &&
assertDefined(
ngModuleDef,
`NgModule '${stringify(ngModuleType)}' is not a subtype of 'NgModuleType'.`);

this._bootstrapComponents = maybeUnwrapFn(ngModuleDef!.bootstrap);
this._r3Injector = createInjectorWithoutInjectorInstances(
ngModuleType, _parent,
[
{provide: viewEngine_NgModuleRef, useValue: this}, {
provide: viewEngine_ComponentFactoryResolver,
useValue: this.componentFactoryResolver
}
],
stringify(ngModuleType)) as R3Injector;

// We need to resolve the injector types separately from the injector creation, because
// the module might be trying to use this ref in its constructor for DI which will cause a
// circular error that will eventually error out, because the injector isn't created yet.
this._r3Injector._resolveInjectorDefTypes();
this.instance = this.get(ngModuleType);
}

6. Angular 系列文章

最新更新以及更多 Angular 相关文章请访问 Angular 合集 | 鹏叔的技术博客

5. 参考文章

深度解析@angular/platform-browser-dynamic
angular 源码分析之 platformBrowserDynamic
Angular Platforms in depth. Part 1