Angular依赖注入小解

1、什么是依赖注入?
答:依赖注入,就是在开发一个大型功能时,将所需要的一些辅助性的工具、他人已经开发好的小功能、服务器数据请求以一种依赖的方式注入到正在开发的功能中的操作
 
2、依赖注入的流程?
答:
   a、通过@Injectable装饰器定义一个可注入的服务类,如果是非类的服务则首先需要利用InjectionToken生成注入令牌
    注:@Component\@Directive\@Pipe都是@Injectable的子类,@Component是对@Directive的扩展加了一些组件特殊属性
    b、注册服务的方式有多种,可以在根模块AppModule的中或者根组件中,或者具体的功能组件中的providers提供商装饰器参数对象属性中进行注册,根级可在应用内容共享,组件级只针对组件本身及其子组件
    c、完成上述步骤后在需要使用的依赖服务的组件类中,以构造函数参数的形式进行注入,或者将组建类内置的注入器Injector手动注入到构造函数参数中,在构造函数中手动注入需要的服务如:this.injector.get(‘服务名’)
    注:手动注入方式也必须完成前两步操作
 
3、非类依赖服务
答:非类依赖服务即基础数据类型值、对象、集合等类型的值,此类服务注册时
    a、需要使用InjectionToken生成注册令牌,即:injectToken = new InjectionToken('描述’); 
    b、然后在providers数组中注册依赖服务,即:{provide: injectToken, useValue: '服务内容’}
    c、在需要使用此类服务的类组件中,在构造函数的参数中使用注入注解@Inject注入,即:@Inject(injectToken) service: serviceType
 
4、在提供商provider中注册服务的方式
答:
    a、{provide: serviceClass, useClass:serviceClass},使用服务类进行注册服务,即注册通过@Injectable定义的类
    b、{provide: serviceClass, useFactory: serviceClassFactory, deps: [param1, param2,...]},由于注册此依赖服务时需要一些其他数据的辅助,而这些数据又可能是异步获取的,因此使用服务类工厂的方式,在工厂内部进行服务类依赖的处理并返回此服务类的实例,deps为工厂函数依赖的服务项,会以工厂函数参数的形式传入
    c、{provide: serviceClass, useExisting: serviceClassNewName},当你有newLogger和oldLogger两个日志服务时,希望oldLogger服务更换为newLogger,可以使用useExisting设置类的别名,在令牌不变的情况下使用同一个依赖服务类
    d、{provide: serviceInjectionToken, useValue: ‘简单值’},用于注册非类服务依赖,需要使用new InjectionToken生成注册令牌,即:serviceInjectionToken
 
5、组件类构造函数定义可选参数
答:@Optional()定义可选项,未提供时值为null
 
6、多级依赖注入树
答:多级依赖注入树,是与组件树平行的一颗针对依赖注入的树,与组件一一对应,每一个组件都有其自己的注入器,当需要一个服务时会从当前组件位置的注入器逐级向上查找,直到找到最近的一个拥有此服务的注入器或者是到达根注入器位置时停止查找,因此查找依赖服务时会出现注入器拦截的情况,如果在中间层级的注入器找到所需的服务就会停止查找,不会再继续向上查找,也就是所谓的注入器拦截
 
7、多级注入器-两个注入器层次结构:ModuleInjector和ElementInjector
答:
    a:ModuleInjector层次结构是由@NgModule或@Injectable()注解在此层次中配置,
        》遍历的@NgModule()的imports属性中模块的providers属性的值的扁平化内容
        》由@Injectable()注解的属性provideIn配置,并且provideIn配置的优先级要高于imports中providers配置的
        》子ModuleInjector是由惰性加载的@Ngmodules创建的,是应用根注入器的一个子注入器,通过路由器惰性加载的模块,路由器会将根注入器的所有提供者添加到惰性模块的子注入器中
        》在rootMoudleInjector(bootstrapModule()以AppModule为配置创建的)之上还有两个注入器分别是:
            平台注入器:由PlatformBrowserDynamic()方法以platformModule为配置创建的
            顶级注入器:即NullInjector,在遍历了整个注入器树无果后会进入NullInjector注入器进行查找,如果没有找到会报错,如果使用了@Optional()修饰符,则会返回null
    b:ElementInjector层次结构是由DOM元素上隐式创建的,除非在@Directive()或@Component()的providers或viewProviders属性中进行配置,否则默认情况下为空
 
8、依赖注入解析规则之ModuleInjector和ElementInjector
答:组件是一种特殊的指令,因此指令也可以配置提供商,所以在组件或者指令中配置提供商时,该提供商就是该组件或者指令的ElementInjector解析分两个阶段:
    第一阶段:使用自身的ElementInjector来检查是否满足依赖,如果不满足会继续向上查找,如果直到根ElementInjector都没有满足就会返回到当前请求发起的组件,来到第二个阶段
    第二阶段:先使用自身的ModuleInjector来检查是否满足依赖,如果不满足会继续向上查找,如果直到根ModuleInjector都没有找到就会报错
 
9、依赖注入解析修饰符
答:@Self\@SkipSelf\@Host只是针对ElementInjector注入器层次的查找做出开始位置和结束位置的限制,而不会去限制ModuleInjector注入器层次的查找
    @Optional:标记依赖为可选项,如果没有找到就会返回null
    @Self:只在当前组件或指令中查找依赖
    @SkipSelf:跳过组件自身的ElementInjector,从其父级的ElementIjector开始查找
    @Host:将当前组件视为根ElementInjector来进行查找,并在在遇到组件模版实例<#VIEW>标签时停止
 
10、providers和viewProviders
答:
    a、组件在插入到dom中时会有一个<#VIEW>标签,此标签既组件模版的实例,查找时会先从组件模版实例上查找
    a、providers提供的服务会在组件元素上注入
    b、viewProviders提供的服务会在组件的模版实例上注入,只在模版实例内可见,在组件元素内模版实例外不可见
    即:<app-select @NgModule() @Inject(service) => value>
                <#VIEW @Inject(viewService) => viewValue></#VIEW>
            </app-select>
    e、使用ng-content投影到组件中的内容,对于在模版实例上注入(viewProviders)的服务不可见,providers属性注入的服务可见
 
11、forwardRef(前向引用)
答:使用此方法来打破循环引用的问题,引用尚未定义的引用,如:{provide: Parent, useExiting: forwardRef(() => Class)},或者在类的构造器函数参数中使用:@Inject(forwardRef(() => LockClass)) lock: LockClass
 
12、单例服务及服务的作用域
    a、单例服务的创建方式有两种:
        》在服务类的@Injectable装饰器中指定provideIn: ‘root’的方式将该服务提供到全应用内(推荐使用此方式,利于摇树优化
        》把该服务提供到根模块或者某些只会被根模块导入的模块的providers数组中
     b、在服务类的@Injectable装饰器中指定provideIn: SomeModule的方式将该服务提供到SomeModule模块作用域内可用
     c、在服务类的@Injectable装饰器中指定provideIn: ‘any’时,会针对所有急性加载的模块共享同一个单例服务,而对于惰性加载的模块则各自拥有各自独立的单例
     d、forRoot模式:
        》通过在模块中定义静态方法forRoot,并在返回的对象中定义providers数组,将需要全局单例的提供者添加入其中,并在根模块中使用此模块的forRoot方法导入该模块和提供者,如:
            @NgModule() export class OtherModule{
                static forRoot(config?): ModuleWithProviders<OtherMoudle> {
                    return {
                        ngModule: OtherModule,
                        providers: [service1, service2...]
                    }
                }
                //根模块导入使用
                @NgModule(
                    imports: [
                        OtherModule.forRoot(config)
                    ]
                )
        》在其他子模块中导入时则直接导入模块即可,无需调用此模块的forRoot()方法
 
 
 
posted @ 2021-12-24 18:19  满怀期望的人  阅读(204)  评论(0编辑  收藏  举报