[译] 关于 Angular 依赖注入你需要知道的
如果你之前没有深入了解 Angular 依赖注入系统,那你现在可能认为 Angular 程序内的根注入器包含所有合并的服务提供商,每一个组件都有它自己的注入器,延迟加载模块有它自己的注入器。
但是,仅仅知道这些可能还不够呢?
不久前有个叫 Tree-Shakeable Tokens feature 被合并到 master 分支,如果你和我一样充满好奇心,可能也想知道这个 feature 改变了哪些东西。
所以现在去看看,可能有意外收获嗷。
注入器树(Injector Tree)
大多数开发者知道,Angular 会创建根注入器,根注入器内的服务都是单例的。但是,貌似还有其他注入器是它的父级。
作为一名开发者,我想知道 Angular 是怎么构建注入器树的,下图是注入器树的顶层部分:
这不是整棵树,目前还没有任何组件呢,后面会继续画树的剩余部分。但是现在先看下根注入器 AppModule Injector,因为它是最常使用的。
根注入器(Root AppModule Injector)
我们知道 Angular 程序根注入器 就是上图的 AppModule Injector,上文说了,这个根注入器包含所有中间模块的服务提供商,也就是说(注:不翻译):
If we have a module with some providers and import this module directly in AppModule or in any other module, which has already been imported in AppModule, then those providers become application-wide providers.
根据这个规则,上图中 EagerModule2 的 MyService2 也会被包含在根注入器 AppModule Injector 中。
ComponentFactoryResolver 也会被 Angular 添加 到这个根注入器对象内,它主要用来创建动态组件,因为它存储了 entryComponents 属性指向的组件数组。
值得注意的是,所有服务提供商中有 Module Tokens,它们都是被导入模块的类名。后面探索到 tree-shakeable tokens 时候,还会回到这个 Module Tokens 话题。
Angular 使用 AppModule 工厂函数来实例化根注入器 AppModule Injector,这个 AppModule 工厂函数就在所谓的 module.ngfactory.js 文件内:
我们可以看到这个工厂函数返回一个包含所有被合并服务提供商的模块对象,所有开发者都应当熟悉这个(注:可以查看 译 Angular 的 @Host 装饰器和元素注入器)。
Tip: If you have angular application in dev mode and want to see all providers from root AppModule injector then just open devtools console and write:
ng.probe(getAllAngularRootElements()[0]).injector.view.root.ngModule._providers
还有很多其他知识点,我在这里没有描述,因为官网上已经谈到了:
angular.io/guide/ngmod…
angular.io/guide/hiera…
Platform Injector
实际上,根注入器 AppModule Injector 有个父注入器 NgZoneInjector,而它又是 PlatformInjector 的子注入器。
PlatformInjector 会在 PlatformRef 对象初始化的时候,包含内置的服务提供商,但也可以额外包含服务提供商:
const platform = platformBrowserDynamic([ {
provide: SharedService,
deps:[]
}]);
platform.bootstrapModule(AppModule);
platform.bootstrapModule(AppModule2);
这些额外的服务提供商是由我们开发者传入的,且必须是 StaticProviders。如果你不熟悉 StaticProviders 和 Provider 两者间的区别,可以查看这个 StackOverflow 的答案。
Tip: If you have angular application in dev mode and want to see all providers from Platform injector then just open devtools console and write:
ng.probe(getAllAngularRootElements()[0]).injector.view.root.ngModule._parent.parent._records;
// to see stringified value use
ng.probe(getAllAngularRootElements()[0]).injector.view.root.ngModule._parent.parent.toString();
尽管根注入器以及其父注入器解析依赖的过程清晰明了,但是组件级别的注入器如何解析依赖却让我很困惑,所以,我接着去深入了解。
EntryComponent and RootData
我在上文聊到 ComponentFactoryResolver 时,就涉及到 entryComponents 入口组件。这些入口组件会在 NgModule 的 bootstrap 或 entryComponents 属性中声明,@angular/router 也会用它们动态创建组件。
Angular 会为所有入口组件创建宿主工厂函数,这些宿主工厂函数就是其他视图的根视图,也就是说(注:不翻译):
Every time we create dynamic component angular creates root view with root data, that contains references to elInjector and ngModule injector.
function createRootData(
elInjector: Injector, ngModule: NgModuleRefhttps://juejin.im/post/5b72f3bce51d4566295cf160
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
来源:https://www.f2ecoder.net/808.html