下面会从 Angular 的源码分析依赖注入中的的装饰器,基于 angular@4.1.0-beta.1
Angular 依赖注入过程主要有下面几个重要的部分组成:
- Inject 和 Injectable 装饰器
- 解析提供商,构造注入器
- 获取实例化对象
Angular 依赖注入中的一些重要的概念:
Provider :
提供商,下面就是一个Proviver,一共有5种构造提供商的方式:TypeProvider, ValueProvider, ClassProvider, ExistingProvider, FactoryProvider
{ provide: Logger, useClass: Logger }
Token :
令牌,提供商中的第一个参数就是Token,在查找依赖时作为 key 使用。
Injector :
注入器,用于解析依赖和创建对象。
Example
class Engine {
start() {
console.log('engine start');
}
}
class Car {
engine: Engine;
constructor(@Inject(Engine) engine) {
this.engine = engine;
}
open() {
this.engine.start();
console.log('car open');
}
}
let inj = ReflectiveInjector.resolveAndCreate([
Car,
Engine
]);
let car = inj.get(Car);
car.open();
1. 装饰器
下面主要讲 Inject 和 Injectable 两个装饰器。
1.1 Inject 装饰器
构造器中的@Inject()指明了需要注入的对象类型,这里使用TypeScript的装饰器模式。
Inject装饰器会将 构造器 => [依赖0, 依赖1 …] 这种 Map 数据结构保存在全局变量中,而不是装饰的对象上。而且只是保存了这种数据关系,并不会实际创建对象。,Injector 会从前面保存的数
简单地说就是,Inject 装饰器在 window[‘__core-js_shared__’] 这个对象中保存了一个 Map = { Car() => [ Engine() ] }, 在后面在解析 Provider 的时候,Injector 就是从这里寻找对象的依赖。
当然实际还要复杂的多。
下面将一步步介绍 Inject 注入器做了哪些事,看源码:
在上面的代码编译成 JS 文件时,可以看到如下代码,__param() 函数传入 paramIndex 和 decorator 两个参数,其中 decorator 由 metadata_1.Inject() 得到,这个函数先放到后面。 __decorate 需要四个参数,因为装饰器可以用在函数或属性上面,因此需要对不同的目标作不同的处理。
在本例中只传入了2个参数,分别是 decorators 和 target ( Car ), 因此 r = target. 后面判断是否存在Reflect这个对象,因为这个对象是 es7 中的提案,一般浏览器中不存在,需要使用 polyfills 的方式引入。如果不存在,则对 decorators 中的每一个值执行 d(r),那么 d(r) 这个方法到底做了什么呢?
decorators 中的每一个 decorator 都是 __param() 的处理结果,而它的返回值是一个匿名函数,因此 d(r) 实际执行了这个匿名函数,匿名函数里面又执行了 decorator(). decorator是什么函数呢?
var __decorate = (this && this.__decorate) || func