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() 类装饰器,这样所有服务都遵循同样的规则,一致性

原文地址:https://www.cnblogs.com/fangdongdemao/p/14187022.html