依赖项
redi 支持以下几种依赖项:
- 类依赖项
- 值依赖项
- 工厂依赖项
- 异步依赖项
类依赖项
类可以直接作为依赖项:
class AuthService {}
也是可以一个符合 ClassDependencyItem
接口的对象:
export interface ClassDependencyItem<T> {
useClass: Ctor<T>;
lazy?: boolean;
}
惰性实例化
你可以将 lazy
设置为 true
,这样该依赖就不会在它的依赖实例化时被实例化,而是等到实际被调用之后才实例化。
interface IHttpInterceptor {
intercept(): void;
}
const IHttpInterceptor = createIdentifier<IHttpInterceptor>("http-interceptor");
class AuthHttpInterceptor implements IHttpInterceptor {
intercept(): void {}
}
class FileListService {
constructor(
@Inject(IHttpInterceptor) private readonly httpI: IHttpInterceptor,
) {}
request() {
this.httpI.intercept();
}
}
const injector = new Injector([
[FileListService],
[IHttpInterceptor, { useClass: AuthHttpInterceptor, lazy: true }],
]);
在上面的例子中,FileListService
依赖了 AuthHttpInterceptor
,从注入器获取 FileListService
时该类会实例化,但是 AuthHttpInterceptor
并未实例化:
const fileListService = injector.get(FileListService);
只有实际访问了 AuthHttpInterceptor 方法时才会实例化:
fileListService.request(); // -> this.httpI.intercept()
通过延迟实例化部分依赖,可以降低应用初始化时的性能开销。
请确保被延迟实例化的类的构造函数中不存在副作用,否则会导致应用不能按你期望的方式运行。
值作为依赖项
值依赖项是一个符合 ValueDependencyItem
接口的对象:
export interface ValueDependencyItem<T> {
useValue: T;
}
该值将会直接被提供给依赖它的依赖。
工厂函数依赖项
工厂函数依赖项将实例化的过程重新交还到开发者的手中,这样就有了更高的灵活性,并且它可以像类依赖项一样声明自己的依赖项。
工厂函数依赖项应当是一个符合下面接口的对象:
export interface FactoryDependencyItem<T> {
useFactory: (...deps: any[]) => T;
deps?: FactoryDep<any>[];
}
例子:
interface I18NNumberTranspiler {
transpile(num: number): string
}
const I18NNumberTranspiler = createIdentifier<I18NNumberTranspiler>(
'i18n-number'
)
class ChineseNumberTranspiler implements I18NNumberTranspiler {}
class EnglishNumberTranspiler implements I18NNumberTranspiler {}
class I18NService {
isChinese(): boolean
}
const injector = new Injector([
[I18NService],
[
I18NNumberTranspiler,
{
useFactory: (i18nService: I18NService) => {
return i18nService.isChinese()
? new ChineseNumberTranspiler()
: new EnglishNumberTranspiler()
},
deps: [I18NService]
},
],
]
已存在的依赖项
有时候你可能需要使用已经存在的依赖项,换句话说,你想要给一个依赖项以别名,这时候你可以使用 ExistingDependencyItem
,它的接口如下所示:
export interface ExistingDependencyItem<T> {
useExisting: DependencyIdentifier<T>;
}
例子:
interface IHttpInterceptor {
intercept(): void;
}
const IHttpInterceptor = createIdentifier<IHttpInterceptor>("http-interceptor");
class AuthHttpInterceptor implements IHttpInterceptor {
intercept(): void {}
}
const INetworkInterceptor = createIdentifier<IHttpInterceptor>(
"network-interceptor",
);
const injector = new Injector([
[IHttpInterceptor, { useClass: AuthHttpInterceptor, lazy: true }],
[INetworkInterceptor, { useExisting: IHttpInterceptor }],
]);
console.log(
injector.get(IHttpInterceptor) === injector.get(INetworkInterceptor),
); // true
异步依赖项
有些时候某些依赖并不需要在加载首屏所需 JS 代码的时候加载,因此你可以通过异步依赖项和 webpack 的 import 的函数来懒加载此依赖。此时,该依赖项必须是一个满足 AsyncDependencyItem
的对象:
export interface AsyncDependencyItem<T> {
useAsync: () => Promise<
T | Ctor<T> | [DependencyIdentifier<T>, SyncDependencyItem<T>]
>;
}
useAsync
函数必须要异步返回一个已经实例化的依赖项,或者是异步依赖项之外的其他依赖项。同时,声明依赖关系时,声明的并不是对应的标识符,而是 AsyncHook
。
例子:
// index.ts
interface IReportService {
report(msg: string): void
}
const IReportService = createIdentifier<IReportService>('report')
class OrderService {
constructor(
@Inject(IReportService)
private readonly reportSrvLoader: AsyncHook<IReportService>
)
public order(): void {
// ...
this.reportSrvLoader.whenReady().then((reportSrv) => reportSrv.report(''))
}
}
const injector = new Injector([
[OrderService],
[
IReportService,
{
useAsync: () => import('./reportService'),
},
],
])
// reportService.ts
export default class ReportService {
report(msg: string): void;
}