angular11源码探索七[服务二]

提供者的类型

  • 类 : useClass
  • 值 : useValue
  • 工厂: useFactory
  • 别名: useExisting

useClass

提供类的实例,使用类提供者

providers :[{ provide: ProductService, useClass: ProductService }]
简写
令牌和类名一起的快捷写法
providers: [ProductService]

这是我们开发很正常这样写的

创建一个服务
@Injectable({
  providedIn: 'root',// 依赖根目录
})
export class TestService {
  sayHello() {
    console.log(`From TestService --> Hello`);
  }
}
// 使用
    constructor(private readonly testService: TestService) {
        testService.sayHello();
    }

useValue

提供简单值时,使用

模块配置

providers :[ {provide:'USE_FAKE', useValue: true}]

或者分开独立写一个文件

const APP_CONFIG = { 
      serviceURL: "www.serviceUrl.comapi", 
      IsDevleomentMode: true 
}; 
 
providers: [{ provide: AppConfig, useValue: APP_CONFIG }]

冻结配置不让它修改

const APP_CONFIG = Object.freeze({ 
      serviceURL: "www.serviceUrl.comapi", 
      IsDevleomentMode: true 
}); 
 
providers: [{ provide: AppConfig, useValue: APP_CONFIG }]

useExisting 别名

的意思是使用已经注册的类型注入到这里(别名),比如下面示例意思是将 ApiService 起个叫 OtherApiService 的别名

正常使用

 providers: [
    {provide: API_URL, useExisting: TestService}
  ]

页面使用
  constructor(
    @Inject(API_URL) private other: TestService
  ) {
     other.getLog();

总而言之,Angular使用现有对象,而不是在useExisting选项中创建新对象。

useFactory

您可以在其构造函数中插入需要参数的服务,或者可以从函数返回的任何东西。

注入参数化服务

import { Injectable } from '@angular/core';

@Injectable()
export class RandomProvider {

  public modulo = 1;

  constructor(modulo: number) {   //Instanciating the class requires parameter
    this.modulo = modulo
  }
  
  getRandomNumber(): number {
    return this.modulo*Math.random();
  }
 
}

注入
  NgModule ({
    ...
    providers: [
    { 
      provide: RandomProvider, 
      useFactory(){
        return new RandomProvider(4);
      }
    }]
    ...
  })

使用
 constructor(
    private random:RandomProvider
  ) {
    console.log(random.getRandomNumber());

注入一个构造值

  NgModule ({
    ...
    providers: [
    { 
      provide: 'RANDOM_ARRAY', 
      useFactory(){
        return [
          Math.random(),
          Math.random(),
          Math.random()
        ];
      }
    }]
    ...
  })  

使用
  constructor( @Inject('RANDOM_ARRAY') private threeRandomValues: number[] ) {
      
  }

如果工厂函数本身具有依赖项,则可以在deps属性中声明它们。

NgModule ({
  ...
    providers: [{
      provide: 'TRUE_RANDOM_SOURCE',
      deps: [HttpClient], //here we declare dependencies of our factory
      useFactory(httpClient: HttpClient) {   // our factory needs an injectable (HttpClient)
        return this.httpClient.get('http://api.example.com/randomval'); //fake URL - does not really work
      }
    }]
  ...
})

案例

@NgModule({
  // ...

  providers: [{
    provide: ProductService,
    useFactory: () => {
      let logger = new LoggerService()
      let dev = Math.random() > 0.5
      if (dev) {
        return new ProductService(logger)
      } else {
        return new AnotherProductService(logger)
      }
    }
  }, LoggerService]
})

// logger.service.ts,一个简单的打印日志的服务
import { Injectable } from '@angular/core'

@Injectable()
export class LoggerService {
  constructor() { }
  log (messgae: string) {
    console.log(messgae)
  }
}

这里有一点需要注意,如果多个组件共用这个服务,那么生成的实例都是相同的,因为工厂方法创建的对象是一个单例对象,工厂方法只会在创建第一个对象的时候被调用一次,然后在整个应用当中所有被注入的服务的实例都是同一个对象

问题一

在方法内部,我们手动实例化了一个 new LoggerService(),意味着工厂方法与这个类是一种强耦合的关系,而我们又声明了 LoggerService 的提供器,所以我们可以采用下面这种方式来解耦,即利用第三个参数 deps 来声明工厂方法所依赖的参数


@NgModule({
  // ...

  providers: [{
    provide: ProductService,
    useFactory: (logger: LoggerService) => {
      let dev = Math.random() > 0.5
      if (dev) {
        return new ProductService(logger)
      } else {
        return new AnotherProductService(logger)
      }
    },
    deps: [LoggerService]
  }, LoggerService]
})

这样一来就不需要我们手动的去实例化(new LoggerService()),这时的 Angular 将会使用 LoggerService 这个提供器来实例化一个 LoggerService 的实例,并将其注入到 ProductService 的工厂方法的参数当中

问题二

我们是根据一个随机数来决定实例化哪个对象,这样测试是可以的,但是在发布的时候就不适用了,通常在这种情况下,我们可以使用一个变量来决定调用哪个方法,然后同样的在 deps 当中进行依赖声明,然后在当作参数的时候传递进去

@NgModule({
  // ...

  providers: [
    {
      provide: ProductService,
      useFactory: (logger: LoggerService, isDev) => {
        if (isDev) {
          return new ProductService(logger)
        } else {
          return new AnotherProductService(logger)
        }
      },
      deps: [LoggerService, 'IS_DEV_ENV']
    }, 
    LoggerService, 
    {
      provide: 'IS_DEV_ENV',
      useValue: false
    }
  ]
})

同样的,可以不单一的注入一个固定的值,也是可以注入一个对象,方便维护

@NgModule({
  // ...

  providers: [
    {
      provide: ProductService,
      useFactory: (logger: LoggerService, appConfig) => {
        if (appConfig.isDev) {
          return new ProductService(logger)
        } else {
          return new AnotherProductService(logger)
        }
      },
      deps: [LoggerService, 'APP_CONFIG']
    }, 
    LoggerService, 
    {
      provide: 'APP_CONFIG',
      useValue: {
        isDev: false
      }
    }
  ]
})

我们举个小案例方便深入理解下

 providers: [
   {
      provide: 'api', useValue: {
        value: true
      }
    }  
 ]
我们在a中修改这个值
    @Inject('api') private api:object
	    api.value=false;
我们在b中查询到这个值被修改了
原文地址:https://www.cnblogs.com/fangdongdemao/p/14176393.html