• angular11源码探索九[服务源码实践理解]


    angular-masterpackagescoresrcdiinterfaceprovider.ts

    useClass

    
    export interface ClassSansProvider {
      useClass: Type<any>;
    }
    export interface ClassProvider extends ClassSansProvider {
      /**
       *一个注入令牌。(通常是' Type '或' InjectionToken '的实例,但也可以是' any 
       */
      provide: any;
    
      /**
      当为true时,注入器返回一个实例数组。这对于允许多重是很有用的
       提供商跨越多个文件,为一个公共令牌提供配置信息。
       */
      multi?: boolean;
    }
    

    useValue

    export interface ValueSansProvider {
      /**
       * 要注入的值
       */
      useValue: any;
    }
    export interface ValueProvider extends ValueSansProvider {
      provide: any;
      multi?: boolean;
    }
    

    useExisting

    useExisting 的意思是使用已经注册的类型注入到这里(别名)

    export interface ExistingSansProvider {
      /**
       *现有的“令牌”要返回。(相当于“injector.get (useExisting)”)
       */
      useExisting: any;
    }
    export interface ExistingProvider extends ExistingSansProvider {
      provide: any;
      multi?: boolean;
    }
    
    

    useFactory

    export interface FactorySansProvider {
      /**
      调用一个函数来为这个“令牌”创建一个值。函数被调用
    *解决了' token '的值在' deps '字段
       */
      useFactory: Function;
        /*
        *用作' useFactory '函数的参数。
        */
      deps?: any[];
    }
    export interface FactoryProvider extends FactorySansProvider {
      provide: any;
      multi?: boolean;
    }
    

    Injectornew操作符的替代品,它可以自动解析

    • 构造函数依赖关系。
    • 在典型的使用中,应用程序代码请求构造函数中的依赖项,它们确实是由Injector解析。
    解析一个提供商数组,并从这些提供商创建一个注入器。
    
    *传入的提供商可以是一个数组
    *或更多提供程序的递归数组。
    let injector = ReflectiveInjector.resolveAndCreate([Car, Engine]);
    // 拿到第一个
    let car = injector.get(Car);
    
    一个扁平化多个嵌套数组和转换个别的过程
    将providers转换为数组
        @Injectable()
         class Engine {
        }
        @Injectable()
        class Car {
          constructor(public engine:Engine) {}
        }
    let providers = ReflectiveInjector.resolve([Car, [[Engine]]]);
    providers.length  //2
    providers[0].key.displayName    // "Car"
    providers[1].key.displayName   // "Engine"
    
    *从之前解析的提供商创建注入器。
    *
    *本API是在性能敏感部件中构造喷油器的推荐方法。
    var providers = ReflectiveInjector.resolve([Car, Engine]);
    var injector = ReflectiveInjector.fromResolvedProviders(providers);
    injector.get(Car)  // Car
    
    解析一个提供商数组,并从这些提供商创建一个子注入器。
    var parent = ReflectiveInjector.resolveAndCreate([ParentProvider]);
    var child = parent.resolveAndCreateChild([ChildProvider]);
    child.get(ParentProvider) instanceof ParentProvider  //true
    child.get(ChildProvider) instanceof ChildProvider // true
    // 牛逼啦  子找父  === 总找父
    child.get(ParentProvider)  //  parent.get(ParentProvider)
    
    • DI 解析 Providers 时,都会对提供的每个 provider 进行规范化处理,即转换成标准的形式
    function _normalizeProviders(
        providers: Provider[], res: NormalizedProvider[]): NormalizedProvider[] {
      providers.forEach(b => {
        if (b instanceof Type) {
          res.push({provide: b, useClass: b} as NormalizedProvider);
    
        } else if (b && typeof b == 'object' && (b as any).provide !== undefined) {
          res.push(b as NormalizedProvider);
    
        } else if (Array.isArray(b)) {
              // 如果是数组,进行递归处理
          _normalizeProviders(b, res);
    
        } else {
          throw invalidProviderError(b);
        }
      });
    
      return res;
    }
    

    在开发过程中我们可能会遇到类似下面这样的问题

    
    @Injectable()
    class Socket {
      constructor(private buffer: Buffer) { }
    }
    
    console.log(Buffer)  // undefined
    
    @Injectable()
    class Buffer {
      constructor(@Inject(BUFFER_SIZE) private size: Number) { }
    }
    
    console.log(Buffer)  // [Function: Buffer]
    
    // 运行后报错
    

    所以在编译阶段「变量声明和函数声明会自动提升,而函数表达式不会自动提升」

    如果要解决上面的问题,最简单的处理方式是交换类定义的顺序,或者还可以使用 Angular 提供的 forward reference 特性,Angular 通过引入 forwardRef 让我们可以在使用构造注入的时候,使用尚未定义的依赖对象类型,

    我们看看forwardRef原理

    允许引用尚未定义的引用。
    *
    *例如,' forwardRef '用于我们需要引用的' token '的目的
    DI已声明,但尚未定义。它也用于我们创建时使用的token
    *查询尚未定义。
    
    export function forwardRef(forwardRefFn: ForwardRefFn): Type<any> {
    // 当调用 forwardRef 方法时,我们只是在 forwardRefFn 函数对象上,增加了一个私有属性__forward_ref__
      (<any>forwardRefFn).__forward_ref__ = forwardRef;
         // 然后覆写了函数的 toString 方法
      (<any>forwardRefFn).toString = function() {
        return stringify(this());
      };
      return (<Type<any>><any>forwardRefFn);
    }
    

    如果所创建的服务不依赖于其他对象,是可以不用使用 @Injectable() 类装饰器,但当该服务需要在构造函数中注入依赖对象,就需要使用 @Injectable() 装饰器,因为只有声明了 @Injectable() 这个装饰器的服务才可以注入其他服务

    推荐的做法不管是否有依赖对象,在创建服务时都使用 @Injectable() 类装饰器,这样所有服务都遵循同样的规则,一致性

  • 相关阅读:
    实验10:Problem D: STL——管道二
    实验10:Problem C: STL——呵呵型自动机
    实验10:Problem B: STL——哈哈型自动机
    实验10:Problem A: STL——整理唱片
    实验9:Problem I: 学生干部虚基类
    hihocoder1994 树与落叶 DFS+前缀和+二分
    [Offer收割]编程练习赛108
    【模板】左偏树(可并堆)
    P2993 [FJOI2014]最短路径树问题 点分治+最短路
    E
  • 原文地址:https://www.cnblogs.com/fangdongdemao/p/14187022.html
Copyright © 2020-2023  润新知