美文网首页
Angular2依赖注入

Angular2依赖注入

作者: monvhh | 来源:发表于2018-10-19 13:45 被阅读0次

    曾自己借助阿里云和hexo搭了个站点,现已废弃,过往写的博客暂挪到此处。


    title: Angular2依赖注入
    subtitle: 小白学Angular2
    date: 2016-11-24 22:53:51
    tags:
    - 技术
    - Angular2
    - 学习笔记


    依赖注入用我这个小白的话说,就是定义一个类的时候,将某些依赖作为类的参数,即:

    var myClass = function( arg1,arg2){
        this.arg1 = arg1;
        this.arg2 = arg2;
    }
    

    初始化该类的时候,就可以将这些依赖当做参数传递进去。
    这样,当arg1和arg2改变的时候,我们可以不用动这个myClass内部的代码。

    这个用法在纯js时代就常用,只是现在叫做依赖注入。目的是为了,解耦。

    angular 依赖注入与 我的myClass的不同在于
    如果arg1和arg2都是类的实例,
    myClass需要先实例化这些类,然后再调用new myClass(instance1,instance2)。

    而Angular,不需要先实例化每一个依赖注入的类。Angular自己会帮你实例化。但是需要以下几步
    构造函数参数中有这些类: constructor(heroService:HeroService)
    这个constructor的类有一个@Component装饰器(这个一般都不必说。。)
    @Component中有这些类的providers信息,即providers:[HeroService]
    对于被依赖的那些类,需要在export class之前加上@Injectable(),否则注入器会报错
    注:服务是可以继承的

    Angular中的显性注入器

    首先这应当是没有必要的,只要正确使用依赖注入,angular自己会管理好注入器的创建和调用。
    当方式如下:

      injector = ReflectiveInjector.resolveAndCreate([Car, Engine, Tires]);
      let car = injector.get(Car);
    

    直接使用注入器Injector的方式

    可以直接使用注入器工作,但不方便阅读,难以理解,所以尽量避免:
    在@Component中添加这些类的providers信息,即providers:[HeroService]
    在constructor中注入Injector(Injector本身就是一个可注入的服务)
    然后在Component中通过injector.get方式获得providers提供的服务(即类HeroService)
    如果get失败会抛出异常,get方法可以带第二个参数,表示如果服务没找到就当做默认值返回。

    注入器的提供商们:

    令牌token:它作为键值key使用,用于定位依赖值,以及注册这个提供商
    providers: [Logger]
    provider definition object:知道如何创建依赖值得配方,有很多方式创建依赖值

    [{ provide: Logger, useClass: Logger }]
    
    1. 备选的类提供商
    [{ provide: Logger, useClass: BetterLogger }]
    

    当请求Logger时,提供BetterLogger
    (不同于别名,见3)

    2. 带依赖的类提供商
    [ UserService,
      { provide: Logger, useClass: EvenBetterLogger }]
    

    因为EvenBetterLogger注入了UserService(??可是为什么不会在EvenBetterLogger中自动调用??还要在调用EvenBetterLogger的地方在provide声明一下这个服务。)

    3. 别名类提供商
    [ NewLogger,
      // Alias OldLogger w/ reference to NewLogger
      { provide: OldLogger, useExisting: NewLogger}]
    

    对比1,1会创建两个实例,(??1在什么时候回用到??需要创建两个实例)

    [ NewLogger,
      // Not aliased! Creates two instances of `NewLogger`
      { provide: OldLogger, useClass: NewLogger}]
    
    4. 值提供商
    [{ provide: Logger, useValue: silentLogger }]
    

    silentLogger是一个对象

    5. 工厂提供商

    在需要动态创建依赖值得情况下,使用此方式

    //deps为该服务需要的依赖,通过注入器传入工厂方法
    export let heroServiceProvider =
      { provide: HeroService,
        useFactory: heroServiceFactory,
        deps: [Logger, UserService]
      };
    
    let heroServiceFactory = (logger: Logger, userService: UserService) => {
      return new HeroService(logger, userService.user.isAuthorized);
    };
    
    //满足的场景
    constructor(
      private logger: Logger,
      private isAuthorized: boolean) { }
    
    getHeroes() {
      let auth = this.isAuthorized ? 'authorized ' : 'unauthorized';
      this.logger.log(`Getting heroes for ${auth} user.`);
      return HEROES.filter(hero => this.isAuthorized || !hero.isSecret);
    }
    

    注:我们为了重复利用导出一个变量捕获了这个工厂提供商:heroServiceProvider。但也可以不用导出再使用,直接在需要该服务的地方的provider里加上即可,同1、2、3、4
    依赖注入令牌
    注入器维护一个内部的令牌-提供商映射表。
    对于类而言,类名就是这个令牌。
    非类依赖

    export interface AppConfig {
      apiEndpoint: string;
      title: string;
    }
    
    export const HERO_DI_CONFIG: AppConfig = {
      apiEndpoint: 'api.heroes.com',
      title: 'Dependency Injection'
    };
    

    TypeScript接口不是一个有效的令牌。
    Angular不支持。
    解释为:在强类型语言中接口是首选的用于查找依赖的主键,再使用依赖注入很奇怪。
    TS在生成JS之后,不会再有接口。
    Opaque Token
    解决方案是使用一个Opaque Token(不透明的令牌)

    import { OpaqueToken } from '@angular/core';
    
    export let APP_CONFIG = new OpaqueToken('app.config');
    

    使用OpaqueToken对象注册依赖的提供商

    providers: [{ provide: APP_CONFIG, useValue: HERO_DI_CONFIG }]
    

    在@Inject的帮助,将这个配置对象注入到需要的构造函数中

    constructor(@Inject(APP_CONFIG) config: AppConfig) {
      this.title = config.title;
    }
    

    可选依赖

    import { Optional } from '@angular/core’;
    
    constructor(@Optional() private logger: Logger) {
      if (this.logger) {
        this.logger.log(some_message);
      }
    }
    

    注:当使用@Optional()的时候,需要为空值做准备

    进阶:

    多级依赖注入器
    angular的多级依赖注入系统支持与组件树并行的嵌套式注入器
    大白话,子组件通过冒泡的方式向上查找需要的服务;
    其次,每个注入器都会把它提供的服务处理成单例,但当我们并不想共享,想为单个组件生成一个实例,则不要把服务注入到父级的以及父级的父级的组件中。

    其他规范:

    建议为每个服务类都添加@Injectable(),无论是否被依赖,出于为了以后可能被依赖着想以及一致性

    @Component和@Directive @Pipe都是InjectableMetadata的子类型,所以没有必要为component添加@Injectable方法

    注入器可以从编译后的Javascript代码中读取类的元数据,并使用构造函数的参数类型信息来决定注入什么。
    (不是每个JavaScript类都有元数据。TS编辑器默认忽视元数据。如果tsconfig.json中的emitDecoratorMetadata编译器选项为true,编译器就会在生成的Javascript中为每个至少拥有一个装饰器的类添加元数据。
    注入器使用一个类的构造元数据来决定依赖类型(??依赖类型有些什么??),该构造元数据就是构造函数的参数类型所标识的。TS为任何带有一个装饰器的类生成这样的元数据,任何装饰器都生成。当然,使用一个合适的Injectable装饰器来标识更有意义。

    相关文章

      网友评论

          本文标题:Angular2依赖注入

          本文链接:https://www.haomeiwen.com/subject/ipmyzftx.html