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;
}
Injector
是new
操作符的替代品,它可以自动解析
- 构造函数依赖关系。
- 在典型的使用中,应用程序代码请求构造函数中的依赖项,它们确实是由
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()
类装饰器,这样所有服务都遵循同样的规则,一致性