Angular如何将运行时更新的配置值传递给根级pluginModule?
核心问题原因
Angular 依赖注入容器在模块初始化阶段解析 useValue 类型的Provider时,会直接将传入的静态值存入注入器,后续修改 environment 对象的属性不会更新注入器中已经缓存的pluginConfig token值,因此plugin拿到的始终是初始化时的默认值。
可行实现方案
方案1:修改pluginModule支持配置动态更新(推荐)
该方案兼容性最好,支持配置变更后的实时响应
第一步 调整plugin内部结构
新增可读写的配置服务,替换固定的useValue注入:
// plugin内部新增 plugin-config.service.ts import { Injectable } from '@angular/core'; import { pluginConfig } from './plugin-config.interface'; @Injectable() export class PluginConfigService { // 如果需要响应配置变更可以用BehaviorSubject包裹 private _config: pluginConfig = {} as pluginConfig; get config(): pluginConfig { return this._config; } updateConfig(newConfig: Partial<pluginConfig>): void { this._config = { ...this._config, ...newConfig }; } }
调整pluginModule的forRoot逻辑:
export class pluginModule { public static forRoot(initialConfig: pluginConfig): ModuleWithProviders<pluginModule> { return { ngModule: pluginModule, providers: [ PluginConfigService, { provide: pluginConfig, useValue: initialConfig } ] }; } }
plugin内部所有用到pluginConfig的地方,统一注入PluginConfigService获取配置值,不要直接注入pluginConfig token。
第二步 主项目中更新配置
在加载运行时配置的服务中注入PluginConfigService,拿到新配置后直接更新即可:
// 主项目的配置加载服务 import { PluginConfigService } from '你的plugin包名'; @Injectable({ providedIn: 'root' }) export class RuntimeConfigService { constructor( private http: HttpClient, private pluginConfigService: PluginConfigService ) {} loadRuntimeConfig() { return this.http.get<RuntimeConfiguration>(`${appUrl}/${this.runtimeConfigJson}`, options).pipe( tap((config: RuntimeConfiguration) => { this.mapRuntimeConfig(config); }) ); } mapRuntimeConfig(runtimeConfig: RuntimeConfiguration): void { environment.pluginEnvironmentUrl = runtimeConfig.pluginEnvironmentUrl; // 直接更新plugin的配置 this.pluginConfigService.updateConfig({ pluginEnvironmentUrl: runtimeConfig.pluginEnvironmentUrl }); } }
方案2:无需修改plugin代码,覆盖Provider
如果没有权限修改plugin的源码,可以用useFactory覆盖pluginConfig的注入逻辑,依赖主项目的运行时配置服务:
- 先在主项目的配置加载服务中新增方法暴露运行时配置
- 在
appModule的providers数组中新增覆盖逻辑,同token的Provider后面声明的会覆盖前面的:
// app.module.ts @NgModule({ imports: [ // 这里可以传空的初始值,后面会被覆盖 pluginModule.forRoot({}) ], providers: [ RuntimeConfigService, { provide: APP_INITIALIZER, useFactory: (configService: RuntimeConfigService) => () => configService.loadRuntimeConfig().toPromise(), deps: [RuntimeConfigService], multi: true }, // 覆盖plugin的配置注入 { provide: pluginConfig, useFactory: (configService: RuntimeConfigService) => { return { pluginEnvironmentUrl: configService.getRuntimeConfig().pluginEnvironmentUrl }; }, deps: [RuntimeConfigService] } ] }) export class AppModule {}
注意:该方案仅适用于APP_INITIALIZER执行完成后才会初始化的plugin组件/服务,如果plugin在模块初始化阶段就同步用到了配置,会出现空值问题
内容的提问来源于stack exchange,提问作者gkrishy
相关产品推荐
相关产品推荐

