Introduction
Angular里面的Injector是分层级的,主要有以下两个层级:
- EnvironmentInjector
- ElementInjector
EnvironmentInjector
EnvironmentInjector层级是通过以下两种方式创建的:
@Injectable({ providedIn: 'xxx' })- xxx 可以是root,platform.providersarray inApplicationConfig
ElementInjector
ElementInjector是创建在每个Dom元素上的,初始时是空的,当我们在@Directive或者@Component中声明了一个provider时,Angular会在ElementInjector中创建一个实例。
ModuleInjector
在基于模块的应用中,每个模块都有一个ModuleInjector,ModuleInjector可以通过如下方式配置:
Injectable({ providedIn: 'xxx' })- xxx 可以是platform,root.providersarray in@NgModule
PlatformInjector
在rootInjector之上,还有两个Injector,分别是:
- 一个额外的EnvironmentInjector - 即
PlatformInjector. - 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!
ElementInjector
Angular会为每个Dom元素隐式创建一个ElementInjector,当我们在@Directive或者@Component中声明了一个provider或者viewProvider时,Angular会在ElementInjector中创建一个实例。
依赖解决规则(查找规则)
当Angular为一个Component或者Directive查找依赖时,遵循如下规则:
- 先查找Component或者Directive自身的ElementInjector,如果找到则返回。否则进入下一步。
- 查找Component或者Directive的Parents的ElementInjector,再查找Parents的Parents的ElementInjector,一直如此直到找到,否则进入下一步。
- 查找Component或Directive的EnvironmentInjector层级,此过程同ElementInjector,一直向上查找,如果找到则返回,否则报错。
此处应该加入一张图。
依赖解决修饰符
上面介绍的依赖解决规则,可以用以下修饰符来改变,你可以从@angular/core中引入这些修饰符:
@Optional- 如果找不到依赖,不会报错,而是返回null。@SkipSelf- 跳过当前ElementInjector,向上查找。@Self- 只在当前ElementInjector中查找依赖,不会向上查找。@Host- 只在当前Component及其Host Element的Injector中查找依赖。
这些修饰符可以分为三类:
- What to do if Angular doesn’t find what you’re looking for, this is
@Optional() - Where to start looking, that is
@SkipSelf. - Where to stop looking,
@Hostand@Self.
使用修饰符
这些修饰符都是在constructor中注入依赖时使用的,比如:
1 | constructor(() private myService: MyService) {} |
关于这些修饰符的详细使用规则,请看这篇。