在 Angular 2 子模块中强制服务实例化(AngularJS 运行 块的替代方案)

Force service instantiation in Angular 2 sub-module (an alternative to AngularJS run block)

我在子模块中有一个服务,它包装了一些第三方模块,实例化并初始化其服务以准备在应用程序中使用。

@Injectable()
class SubmoduleInitializerService {
    constructor (thirdPartyService: ThirdPartyService) {
      thirdPartyService.initialize(...);
      ...
    }
}

@NgModule({
    imports: [ThirdPartyModule],
    exports: [ThirdPartyModule],
    providers: [
        ThirdPartyService,
        SubmoduleInitializerService
    ]
})
class AppSubmodule {}

ThirdPartyService 不直接注入到应用程序中,而是被其他 ThirdPartyModule 单元使用,因此只要 SubmoduleInitializerService 注入与 ThirdPartyService 相同的注入器或父注射器,一切都很好:

export class AppComponent {
    constructor(
      /* DO NOT REMOVE! BAD THINGS HAPPEN! */
      submoduleInitializerService: SubmoduleInitializerService
    ) {}
    ...
}

它被证明是一个糟糕的模式,因为如果 SubmoduleInitializerService 既没有在 class 中也没有在模板中使用(不小心已删除一次)。

基本上 AppSubmodule 模块需要替代 Angular 1.x angular.module(...).run(...) 块。

这里有哪些选项?

APP_INITIALIZER(未记录)服务在 Angular2 中相当好地扮演了 AngularJS config/run 块的角色(不包括异步初始化的特性)。

对于刚刚急切实例化SubmoduleInitializerService的noop初始化块是:

@NgModule({
    imports: [ThirdPartyModule],
    exports: [ThirdPartyModule],
    providers: [
        ThirdPartyService,
        SubmoduleInitializerService,
        {
            provide: APP_INITIALIZER,
            useFactory: () => () => {},
            deps: [SubmoduleInitializerService],
            multi: true
        }
    ]
})
class AppSubmodule {}

由于 APP_INITIALIZER 是多供应商,它允许每个应用程序有多个初始化函数,这些函数遵循模块的加载顺序。

对于同步初始化,更短(并且可能更合适)的替代方法是将服务注入模块的构造函数:

@NgModule({
    imports: [ThirdPartyModule],
    exports: [ThirdPartyModule],
    providers: [
        ThirdPartyService,
        SubmoduleInitializerService
    ]
})
class AppSubmodule {
    constructor(sis: SubmoduleInitializerService) {}
}

中所述,APP_INITIALIZER 也与 config 块共享一些特征,因为它用于在组件初始化之前配置服务并且容易受到竞争条件的影响(例如,由于 APP_INITIALIZER 用于配置 Router,因此将其注入另一个 APP_INITIALIZER 将导致循环依赖)。