特性模块中Injection Token注入失败:StaticInjectorError无提供者求助
嘿,我之前也踩过类似的坑,这大概率是Angular注入器层级和NgRx Effects的注入上下文导致的问题,咱们一步步拆解解决:
问题根源分析
Angular的注入器是分层的,而NgRx Effects默认会使用根注入器实例化。当你在惰性加载的特性模块中单独提供REST_ENDPOINTS Token时,这个Token只存在于特性模块的子注入器中;如果你的Feature Effects还是由根注入器创建,自然找不到特性模块里的提供者,就会抛出StaticInjectorError。
另外还要排除一个基础问题:你有没有在多个地方重复定义REST_ENDPOINTS Token?如果特性模块自己又创建了一个同名Token,那和根模块的Token根本不是同一个,注入肯定失败。
分步解决方案
1. 确保Token全局唯一且正确导出
先检查你的Token定义文件,必须是在共享位置导出,全局和特性模块都引用同一个Token:
// 比如在shared/tokens/rest-endpoints.token.ts import { InjectionToken } from '@angular/core'; export const REST_ENDPOINTS = new InjectionToken<Record<string, string>>('REST_ENDPOINTS');
绝对不要在特性模块里再重新创建new InjectionToken(...),必须直接导入这个共享的Token。
2. 配置特性模块的Effects和Token提供者
在特性模块中,一定要用EffectsModule.forFeature()来注册当前模块的Effects,这样Angular会用特性模块的子注入器来实例化这些Effects,就能拿到模块内提供的Token了:
// feature/feature.module.ts import { NgModule } from '@angular/core'; import { EffectsModule } from '@ngrx/effects'; import { REST_ENDPOINTS } from '../../shared/tokens/rest-endpoints.token'; import { FeatureEffects } from './feature.effects'; @NgModule({ imports: [ // 关键:用forFeature注册特性模块的Effects EffectsModule.forFeature([FeatureEffects]) ], providers: [ { provide: REST_ENDPOINTS, useValue: { // 特性模块专属的端点配置 featureUserApi: '/api/feature/users', featureOrderApi: '/api/feature/orders' } } ] }) export class FeatureModule {}
3. 确保Effects中正确注入Token
在FeatureEffects里,别漏了@Inject()装饰器(因为Token是自定义的,不是类):
// feature/feature.effects.ts import { Injectable, Inject } from '@angular/core'; import { Actions, createEffect, ofType } from '@ngrx/effects'; import { REST_ENDPOINTS } from '../../shared/tokens/rest-endpoints.token'; import { HttpClient } from '@angular/common/http'; import { map } from 'rxjs/operators'; @Injectable() export class FeatureEffects { loadFeatureUsers$ = createEffect(() => this.actions$.pipe( ofType('[Feature] Load Users'), map(() => this.http.get(`${this.endpoints.featureUserApi}`)) ) ); constructor( private actions$: Actions, private http: HttpClient, // 必须用@Inject指定Token @Inject(REST_ENDPOINTS) private endpoints: Record<string, string> ) {} }
额外检查:模块加载方式
如果你的特性模块是惰性加载的,确保路由配置正确(用loadChildren),这样特性模块才会创建独立的子注入器;如果是即时加载的,根注入器会合并所有模块的提供者,这时候如果根模块和特性模块都提供了同一个Token,根模块的配置会覆盖特性模块的——如果这不是你想要的,可能需要考虑用@Optional()或者分模块创建不同的Token。
内容的提问来源于stack exchange,提问作者KiraAG

