在 Angular 2 子模块中强制服务实例化(AngularJS 运行块的替代方案)
我在子模块中有一个服务,它包装了一些第三方模块,实例化并初始化其服务以准备在应用程序中使用.
I have a service in sub-module that wraps some third-party module, instantiates and initializes its service to prepare for use within app.
@Injectable()
class SubmoduleInitializerService {
constructor (thirdPartyService: ThirdPartyService) {
thirdPartyService.initialize(...);
...
}
}
@NgModule({
imports: [ThirdPartyModule],
exports: [ThirdPartyModule],
providers: [
ThirdPartyService,
SubmoduleInitializerService
]
})
class AppSubmodule {}
ThirdPartyService
不会直接注入应用程序,而是被其他ThirdPartyModule
单元使用,因此只要SubmoduleInitializerService
被注入到相同的注入器作为 ThirdPartyService
或父注入器,一切都很好:
ThirdPartyService
isn't injected in app directly but is used by other ThirdPartyModule
units, so as long as SubmoduleInitializerService
is injected in the same injector as ThirdPartyService
or parent injector, everything is fine:
export class AppComponent {
constructor(
/* DO NOT REMOVE! BAD THINGS HAPPEN! */
submoduleInitializerService: SubmoduleInitializerService
) {}
...
}
它被证明是一个糟糕的模式,因为如果 SubmoduleInitializerService
既没有在类中也没有在模板中使用(是偶然的),那么它为什么应该留在 AppComponent
中并不明显已经删除一次).
It was proven to be a lousy pattern because it is not obvious why SubmoduleInitializerService
should stay injected in AppComponent
if it's not used neither in class nor in template (was accidentally removed once already).
基本上 AppSubmodule
模块需要替代 Angular 1.x angular.module(...).run(...)
块.
Basically AppSubmodule
module needs an alternative to Angular 1.x angular.module(...).run(...)
block.
这里有哪些选项?
APP_INITIALIZER
(未记录的)服务在 Angular 2 中很好地扮演了 AngularJS 配置/运行块的角色(不包括异步初始化的功能).
APP_INITIALIZER
(undocumented) service plays the role of AngularJS config/run blocks reasonably well in Angular 2 (not counting the feature of asynchronous initialization).
对于只是急切地实例化 SubmoduleInitializerService
的 noop 初始化块,它是:
For noop intialization block that just eagerly instantiates SubmoduleInitializerService
it is:
@NgModule({
imports: [ThirdPartyModule],
exports: [ThirdPartyModule],
providers: [
ThirdPartyService,
SubmoduleInitializerService,
{
provide: APP_INITIALIZER,
useFactory: () => () => {},
deps: [SubmoduleInitializerService],
multi: true
}
]
})
class AppSubmodule {}
由于 APP_INITIALIZER
是多提供者,它允许每个应用程序按照模块加载的顺序有多个初始化函数.
Since APP_INITIALIZER
is multi-provider, it allows to have several initialization functions per application that follow the order in which the modules are being loaded.
对于同步初始化来说,更短(也可能更合适)的替代方法是将服务注入到模块的构造函数中:
For synchronous initialization the shorter (and probably more appropriate) alternative is to inject the service into module's constructor:
@NgModule({
imports: [ThirdPartyModule],
exports: [ThirdPartyModule],
providers: [
ThirdPartyService,
SubmoduleInitializerService
]
})
class AppSubmodule {
constructor(sis: SubmoduleInitializerService) {}
}
如此答案所述,APP_INITIALIZER
与 config 块也是如此,因为它用于在组件初始化之前配置服务并且容易受到竞争条件的影响(例如,由于
APP_INITIALIZER
用于配置 Router
,将其注入另一个 APP_INITIALIZER
将导致循环依赖).
As explained in this answer, APP_INITIALIZER
shares some traits with config
block, too, because it is used to configure services prior to component initialization and is susceptible to race conditions (for example, since APP_INITIALIZER
is used to configure Router
, injecting it into another APP_INITIALIZER
will result in circular dependency).