0%

angular-di-hierarchical-injectors

Introduction

Angular里面的Injector是分层级的,主要有以下两个层级:

  • EnvironmentInjector
  • ElementInjector

EnvironmentInjector

EnvironmentInjector层级是通过以下两种方式创建的:

  1. @Injectable({ providedIn: 'xxx' }) - xxx 可以是 root, platform.
  2. providers array in ApplicationConfig

ElementInjector

ElementInjector是创建在每个Dom元素上的,初始时是空的,当我们在@Directive或者@Component中声明了一个provider时,Angular会在ElementInjector中创建一个实例。

ModuleInjector

在基于模块的应用中,每个模块都有一个ModuleInjector,ModuleInjector可以通过如下方式配置:

  1. Injectable({ providedIn: 'xxx' }) - xxx 可以是 platform, root.
  2. providers array in @NgModule

PlatformInjector

rootInjector之上,还有两个Injector,分别是:

  1. 一个额外的EnvironmentInjector - 即PlatformInjector.
  2. NullInjector.

注意:PlatformInjector 是为了多个Angular app服务的,对于基于Nx的超大型Angular项目,一个项目中可能包含多个app(比如使用了Module Federation)而root injector是针对app的,如果要跨app共享服务,那么就要使用PlatformInjector了。

看一下Angular的启动代码(位于src/main.ts文件中):

1
bootstrapApplication(AppComponent, appConfig);

当执行上述代码时,Angular首先创建一个Platform Injector(也是Environment Injector),然后创建一个Platform Injector的child Injector,也就是root Environment Injector。而在Platform Injector之上,还有一个额外的NullInjector。

所以NullInjector是整个Injector链条的顶端,我们在查找一个依赖时,会顺着这个链条向上查找,如果到了NullInjector还没找到,则会抛出异常。相信很多同学都遇到过下面的错误吧,仔细看报错信息第一行,就是NullInjector!
NullInjector

ElementInjector

Angular会为每个Dom元素隐式创建一个ElementInjector,当我们在@Directive或者@Component中声明了一个provider或者viewProvider时,Angular会在ElementInjector中创建一个实例。

依赖解决规则(查找规则)

当Angular为一个Component或者Directive查找依赖时,遵循如下规则:

  1. 先查找Component或者Directive自身的ElementInjector,如果找到则返回。否则进入下一步。
  2. 查找Component或者Directive的Parents的ElementInjector,再查找Parents的Parents的ElementInjector,一直如此直到找到,否则进入下一步。
  3. 查找Component或Directive的EnvironmentInjector层级,此过程同ElementInjector,一直向上查找,如果找到则返回,否则报错。

此处应该加入一张图。

依赖解决修饰符

上面介绍的依赖解决规则,可以用以下修饰符来改变,你可以从@angular/core中引入这些修饰符:

  1. @Optional - 如果找不到依赖,不会报错,而是返回null
  2. @SkipSelf - 跳过当前ElementInjector,向上查找。
  3. @Self - 只在当前ElementInjector中查找依赖,不会向上查找。
  4. @Host - 只在当前Component及其Host Element的Injector中查找依赖。

这些修饰符可以分为三类:

  1. What to do if Angular doesn’t find what you’re looking for, this is @Optional()
  2. Where to start looking, that is @SkipSelf.
  3. Where to stop looking, @Host and @Self.

使用修饰符

这些修饰符都是在constructor中注入依赖时使用的,比如:

1
constructor(@Optional() private myService: MyService) {}

关于这些修饰符的详细使用规则,请看这篇