Angular DI:如何让组件使用自身模块的同名注入令牌提供者?
Angular依赖注入(DI)模块级提供者覆盖问题解决方案
问题原因
Angular中非懒加载模块的providers会被合并到根注入器。当Module1导入Module2时,两个模块的提供者都会被添加到根注入器,同名的InjectionToken会被后注册的提供者覆盖(此处Module1的提供者覆盖了Module2的)。Cmp2的注入器会向上查找根注入器,因此拿到的是Module1的配置。
解决方案
方案1:将Module2改为懒加载模块
懒加载模块会创建独立的注入器,其提供者不会与根注入器合并,模块内的组件会优先从自身模块的注入器获取依赖。
修改Module1的导入方式,通过路由懒加载实现:
// Module1的NgModule配置 @NgModule({ imports: [ RouterModule.forChild([ { path: 'cmp2', loadChildren: () => import('./module2').then(m => m.Module2) } ]) ], declarations: [Cmp1], providers: [ { provide: PERMISSION_SERVICE, useFactory: () => ({ service: "service1" }) } ] }) export class Module1 {}
当访问对应路由加载Module2时,Cmp2会获取到Module2中定义的PERMISSION_SERVICE。
方案2:封装组件级提供者(推荐,无需修改模块加载方式)
将提供者封装为可复用常量,避免逐个组件重复配置:
首先在Module2中定义复用的提供者数组:
// module2.ts export const MODULE2_PERMISSION_PROVIDERS = [ { provide: PERMISSION_SERVICE, useFactory: () => ({ service: "service2" }) } ];
然后在Module2的所有组件中引用该数组:
@Component({ selector: 'cmp2', template: "CMP2", providers: [MODULE2_PERMISSION_PROVIDERS] }) export class Cmp2 { constructor(@Inject(PERMISSION_SERVICE) private permissionService: any) { console.log('cmp2 ' + permissionService.service); // 输出`cmp2 service2` } }
这种方式通过组件自身的注入器隔离依赖,不受根注入器影响,同时保持代码复用性。
方案3:配合@Self()装饰器明确查找范围
如果组件已定义自身提供者,使用@Self()强制从组件注入器获取依赖,避免向上查找:
@Component({ selector: 'cmp2', template: "CMP2", providers: [MODULE2_PERMISSION_PROVIDERS] }) export class Cmp2 { constructor( @Self() @Inject(PERMISSION_SERVICE) private permissionService: any ) { console.log('cmp2 ' + permissionService.service); } }
内容的提问来源于stack exchange,提问作者Irdis
相关产品推荐
相关产品推荐

