zoukankan      html  css  js  c++  java
  • 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中查询到这个值被修改了
    
  • 相关阅读:
    Linux下SSH的Log文件路径
    Linux下压缩与解压命令tar
    Linux命令之at
    Linux下nice/renice命令小结
    Linux命令详解nice
    LVM---动态调整磁盘容量
    VT100字体
    Linux命令之WC
    for name in loop Shell
    Bind9用view配主从
  • 原文地址:https://www.cnblogs.com/fangdongdemao/p/14176393.html
Copyright © 2011-2022 走看看