我在子模块中有一个服务包装一些第三方模块,实例化并初始化其服务以准备在app中使用.
@Injectable() class SubmoduleInitializerService { constructor (thirdPartyService: ThirdPartyService) { thirdPartyService.initialize(...); ... } } @NgModule({ imports: [ThirdPartyModule],exports: [ThirdPartyModule],providers: [ ThirdPartyService,SubmoduleInitializerService ] }) class AppSubmodule {}
ThirdPartyService不是直接在app中注入,而是由其他ThirdPartyModule单元使用,因此只要SubmoduleInitializerService在与ThirdPartyService或父注入器相同的注入器中注入,一切都很好:
export class AppComponent { constructor( /* DO NOT REMOVE! BAD THINGS HAPPEN! */ submoduleInitializerService: SubmoduleInitializerService ) {} ... }
事实证明这是一种糟糕的模式,因为如果SubmoduleInitializerService既不在类中也不在模板中使用(意外删除一次),那么为什么SubmoduleInitializerService应该在AppComponent中注入并不明显.
基本上,AppSubmodule模块需要替代Angular 1.x angular.module(…).run(…)块.
这里有什么选择?
解决方法
APP_INITIALIZER
(未记录的)服务在Angular 2中相当好地扮演AngularJS配置/运行块的角色(不包括异步初始化的特性).
对于只是急切地实例化SubmoduleInitializerService的noop初始化块,它是:
@NgModule({ imports: [ThirdPartyModule],SubmoduleInitializerService,{ provide: APP_INITIALIZER,useFactory: () => () => {},deps: [SubmoduleInitializerService],multi: true } ] }) class AppSubmodule {}
由于APP_INITIALIZER是多提供程序,因此它允许每个应用程序具有多个初始化函数,这些函数遵循加载模块的顺序.
对于同步初始化,更短(也可能更合适)的替代方法是将服务注入模块的构造函数:
@NgModule({ imports: [ThirdPartyModule],SubmoduleInitializerService ] }) class AppSubmodule { constructor(sis: SubmoduleInitializerService) {} }
正如在this answer中所解释的那样,APP_INITIALIZER也与配置块共享一些特性,因为它用于在组件初始化之前配置服务并且易受竞争条件的影响(例如,因为APP_INITIALIZER用于配置路由器,将其注入另一个APP_INITIALIZER将导致循环依赖).