Angular 19 + Module Federation(ESM) + 独立组件场景下共享@angular/core为peerDependency时出现NG0200循环DI错误
嗨,我之前做Angular 19微前端项目时,刚好踩过一模一样的坑!当时折腾了好一阵才摸透关键问题,咱们一步步来捋:
首先得搞明白,NG0200这个循环DI错误,本质上大多是**@angular/core没真正成为单例**——Host和Remote各自加载了一份@angular/core实例,导致DI容器冲突,进而触发循环依赖报错。
第一步:把Module Federation的共享配置严格对齐
不管是Host还是Remote的Webpack配置(或者Angular CLI生成的mf配置文件),@angular/core的共享规则必须完全一致,尤其是singleton和strictVersion这俩核心参数:
比如Host的shared配置要这么写:
shared: { '@angular/core': { singleton: true, // 强制单例,这是最关键的一步! strictVersion: true, // 版本不匹配直接报错,避免隐式兼容破坏单例 requiredVersion: '^19.2.0', // 和你的项目版本严格对应 includeSecondaries: true // 包含@angular/core的次级模块,防止漏共享 }, // 顺带把@angular/common、@angular/router这些核心依赖也按同样规则配置 '@angular/common': { singleton: true, strictVersion: true, requiredVersion: '^19.2.0', includeSecondaries: true } }
Remote的shared配置必须和Host完全同步!别觉得Remote用了peerDependency就可以随便写,只有两边配置对齐,Webpack才会确保Remote复用Host的@angular/core实例。
第二步:修正独立组件的导出与加载逻辑
因为用的是独立组件,Remote这边的导出要直接给组件本身,别套模块:
// Remote的暴露入口文件比如src/app/exposed.component.ts import { MyStandaloneComponent } from './my-standalone.component'; export default MyStandaloneComponent;
Host这边加载时,别画蛇添足把组件加进模块的declarations里,直接用loadRemoteModule拿到组件就用:
// Host里的加载逻辑 import { loadRemoteModule } from '@angular-architects/module-federation'; async loadMyRemoteComponent() { const RemoteComponent = await loadRemoteModule({ type: 'module', // 必须是module,适配ESM模式 remoteEntry: 'http://localhost:4201/remoteEntry.js', exposedModule: './MyStandaloneComponent' // 和Remote的exposes配置对应 }).then(m => m.default); // 用动态组件方式渲染,比如通过ViewContainerRef创建实例 this.remoteComp = RemoteComponent; }
第三步:把peerDependency的配置做扎实
Remote的package.json里,@angular/core必须老老实实呆在peerDependencies里,绝对不能出现在dependencies或devDependencies里,不然Webpack会偷偷把它打包进去,直接破坏单例:
"peerDependencies": { "@angular/core": "^19.2.0", "@angular/common": "^19.2.0" }, "dependencies": { // 这里绝对不能放@angular/core相关包! }
打包完Remote后,可以去dist目录搜搜有没有@angular/core的代码,要是有就说明配置错了,得回头调整。
第四步:排查循环DI的具体业务场景
如果上面几步都做了还报错,大概率是组件或服务的注入逻辑有问题。比如Remote组件注入了Host的某个服务,而Host的服务又间接依赖了Remote的内容?这时候可以试试:
- 用
@Optional()修饰注入的依赖,避免强制注入导致循环 - 或者用
Injector手动获取依赖,别在构造函数里直接声明 - 检查独立组件的
providers数组,有没有不小心重复提供了Host已有的服务
最后:清缓存!重启服务!
别笑,Webpack的缓存有时候会搞鬼,把Host和Remote的dist目录全删了,再重新启动ng serve,确保是全新构建的环境。
备注:内容来源于stack exchange,提问作者Rajeev kotagiri




