NgRx Effects为何破坏Angular项目的依赖注入?
问题诊断与解决思路
核心原因
NgRx Effects会在Angular应用启动阶段立即初始化,而你的APP_INITIALIZER还在异步执行配置加载(loadAppSettings()和init()),此时ConfigService尚未完成初始化。当UserEffects构造函数注入MyService时,MyService(或其依赖链)间接依赖了未初始化的ConfigService,导致ApplicationInsights尝试读取配置密钥时拿到undefined,触发依赖注入崩溃。
可尝试的解决方向
1. 延迟NgRx Effects初始化,等配置加载完成后再启动
修改AppModule的NgRx初始化逻辑,先不直接注册UserEffects,待APP_INITIALIZER完成配置加载后再手动启动:
- 先调整
imports中的Effects配置:EffectsModule.forRoot([]), - 然后更新
APP_INITIALIZER的工厂函数,注入EffectsRunner和UserEffects,完成配置后启动Effects:
注意:需将{ provide: APP_INITIALIZER, useFactory: ( oneConfigService: OneConfigService, twoConfigService: ConfigService, effectsRunner: EffectsRunner, userEffects: UserEffects ) => () => oneConfigService.loadAppSettings() .then(() => twoConfigService.init()) .then(() => effectsRunner.run(userEffects)), deps: [OneConfigService, ConfigService, EffectsRunner, UserEffects], multi: true },UserEffects添加到AppModule的providers数组中(不再通过EffectsModule.forRoot注册)。
2. 在Effects中用Injector延迟获取服务,避免构造阶段触发依赖链
修改UserEffects,不在构造函数直接注入MyService,而是通过Injector手动获取,在实际执行Effect逻辑时再初始化服务:
import { Injectable, Injector } from '@angular/core'; import { createEffect, ofType, Actions } from '@ngrx/effects'; import { MyService } from './path-to-my-service'; @Injectable() export class UserEffects { private _myService!: MyService; constructor( private actions$: Actions, private injector: Injector ) {} // 示例Effect:触发时才初始化并使用MyService sampleEffect$ = createEffect(() => this.actions$.pipe( ofType('YOUR_ACTION_TYPE'), tap(() => { this._myService = this._myService || this.injector.get(MyService); // 调用MyService的业务方法 }) ) ); }
这种方式能避开Angular启动阶段的依赖链触发,等配置加载完成后再初始化依赖服务。
3. 调整NgRx模块的导入顺序
将StoreModule和EffectsModule的导入放在所有配置相关模块之后,确保配置类Provider先完成初始化:
imports: [ // 先导入所有配置、业务模块 ...其他模块, // 再导入NgRx相关模块 StoreModule.forRoot(reducers, { metaReducers }), EffectsModule.forRoot([UserEffects]), StoreDevtoolsModule.instrument({ maxAge: 25, logOnly: environment.production }), ],
虽然Angular模块导入顺序不绝对决定DI顺序,但调整后可能缓解初始化冲突问题。
4. 检查ConfigService初始化逻辑(若可行)
如果ConfigService的init()方法可改为同步逻辑,或能提前加载配置,可从根源避免异步初始化带来的顺序问题。但如果配置必须异步加载,此方案不适用。
内容的提问来源于stack exchange,提问作者inbanco
相关产品推荐
相关产品推荐

