angular select2源码解析_Angular DI解析 - 1

f1ce0e43b80cbc37c87886ff27418388.png
custom di项目取自Angular源码,删除对InjectFlag,OptionFlag,Parent处理,
本人能力有限,若有说错,理解不对的,望指出~

ReflectiveInjector 与StaticInjector

Q1:为何V5之后选择StaticInjector? Angular团队在V5前后分别设计了两套依赖注入的处理方案,至于为何V5之后选择使用StaticInjector,按照官方说法只是性能考虑,对于兼容性的话其实在现在已不是啥问题,毕竟Reflect应该就只有被淘汰的IE不支持了把?

Q2: 那么ReflectiveInjector是一个残次品吗? 并非如此,Angular团队成员还专门提出了这个方案以供其他场景下使用。

Q3: 两者区别在哪?

  • ReflectiveInjector: 使用了reflect作为反射,简单说就是在每个需要注入的类型上加上了标记,然后将标记存放到全局的一个Map(此Map可以在core.js源码看到)中,需要的时候get一下,代码如下:
// 打标记
function ParamDecorator(cls: any, unusedKey: any, index: number) {
  ...
  // parameters here will be [ {token: B} ]
  Reflect.defineMetadata('parameters', parameters, cls);
  return cls;
}

// 创建注入器
export function resolveReflectiveProviders(providers: Provider[]): ResolvedReflectiveProvider[] {
  // 格式化Providers 转为[{provide: xx, useClass | other: xx},...]
  const normalized = _normalizeProviders(providers, []);
  // 解析provider,得到class ResolvedReflectiveProvider_{ resolvedFactory: [{fn, deps}] }
  const resolved = normalized.map(resolveReflectiveProvider);
  // 整理并存放到Map中
  const resolvedProviderMap = mergeResolvedReflectiveProviders(resolved, new Map());
  return Array.from(resolvedProviderMap.values());
}

// 获取
  get(token: any, notFoundValue: any = THROW_IF_NOT_FOUND): any {
    // 利用token 直接获取需要访问的内容
    return this._getByKey(ReflectiveKey.get(token), null, notFoundValue);
  }

说实话这样的处理方式在get时候很方便,利用reflect可以马上知道需要注入的内容。

  • StaticInjector: 不会解析隐式依赖,相反,它需要开发者为每一个提供商显式指定依赖,它的处理方案则是显示的提供deps数组,在create时存放deps,并在get时候遍历deps取出需要的dep,并注入其中,创建返回开发者
export class StaticInjector implements Injector {
  // 创建Injector
		constructor(providers: StaticProvider[], source: string | null = null) {
        this.source = source;
      		// 为每个Injector单独创建一个Map
        const records = this._records = new Map<any, Record>();
        ...
        // 递归创建Providers,并存放在Map中
        this.scope = recursivelyProcessProviders(records, providers);
    }
		// 获取
		get<T>(token: Type<T> | InjectionToken<T>, notFoundValue?: T): T;
  get(token: any, notFoundValue?: any): any;
  get(token: any, notFoundValue?: any): any {
        const records = this._records;
        let record = records.get(token);
        if (record === undefined) {
            records.set(token, null);
        }
        let lastInjector = setCurrentInjector(this);
        try {
          		// 重点在这,此方法将会解析出对应的内容,具体可以参考我从Angular 中抽出的简化版DI
            return tryResolveToken(token, record, records, notFoundValue);
        } catch (e) {
        } finally {
            setCurrentInjector(lastInjector);
        }
    }
}

// 递归创建Providers,关键一步,此方法中利用compuuteDeps处理相关依赖,并存放到deps数组中,最终会在recursivelyProcessProviders中set到Records下存放
function resolveProvider(provider: SupportedProvider): Record {
    const deps = computeDeps(provider);
    let fn: Function = IDENT;
    let value: any = EMPTY;
    let useNew: boolean = false;
    let provide = resolveForwardRef(provider.provide);
   		...
    return {deps, fn, useNew, value};
}

可以看到,事实上在没有reflect的时候get时需要对所需的deps进行查找,上面的代码是我简化后的,在Angular源码中事实上还有存在Parent Injector的关系链,有些deps会到Parent上查找。传送门

五类Provider简介

具体的使用方法看测试吧!我参照官方给的例子分别对应写了5个测试,我偷个懒就不讲了传送门
  1. ValueProvider
    使用useValue指定的值(可以是具体的对象也可以是string ,number等等之类的值)就是Token依赖的对象。
  2. ExistingProvider
    想获取Token(provide)对应的对象的时候, 获取当前使用useExisting(Token)对应的对象。
  3. StaticClassProvider
    useClass指定的Type创建的对应对象就是Token对应的对象。
  4. ConstructorProvider
    使用给定的provide创建对象,并且Token也是给定的provide。这也是我们用的最多的一种方式
  5. FactoryProvider
    通过调用 useFactory对应的函数,返回Token对应的依赖对象。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值