Angular模块联邦微前端报错:container is undefined求助
Angular Module Federation 微前端路由问题排查
问题概述
拥有两个Angular应用:
- shellApp(微前端宿主),运行在4200端口
- mfeApp(微前端子应用),运行在4201端口
尝试通过Module Federation在shellApp中路由到mfeApp的TestModule,访问http://localhost:4200/mfeApp时抛出typescript error : container is undefined错误。已确认http://localhost:4201/remoteEntry.js可正常访问,但浏览器未请求TestModule,怀疑懒加载配置有误。
核心代码展示
mfeApp 核心代码
app-routing.module.ts
import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; import {AbcComponent} from "./test/abc/abc.component"; const routes: Routes = [ {path: "abc", component: AbcComponent} ]; @NgModule({ imports: [RouterModule.forRoot(routes)], exports: [RouterModule] }) export class AppRoutingModule { }
test.module.ts
import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { AbcComponent } from './abc/abc.component'; @NgModule({ declarations: [ AbcComponent ], imports: [ CommonModule ] }) export class TestModule { }
webpack.config.ts
const ModuleFederationPlugin = require("webpack/lib/container/ModuleFederationPlugin"); const mf = require("@angular-architects/module-federation/webpack"); const path = require("path"); const share = mf.share; const sharedMappings = new mf.SharedMappings(); sharedMappings.register( path.join(__dirname, 'tsconfig.json'), [/* mapped paths to share */]); module.exports = { output: { uniqueName: "mfeApp", publicPath: "auto" }, optimization: { runtimeChunk: false }, resolve: { alias: { ...sharedMappings.getAliases(), } }, experiments: { outputModule: true }, plugins: [ new ModuleFederationPlugin({ library: { type: "module" }, name: "mfeApp", filename: "remoteEntry.js", exposes: { './TestModule': './src/app/test/test.module.ts', }, shared: share({ "@angular/core": { singleton: true, strictVersion: true, requiredVersion: 'auto' }, "@angular/common": { singleton: true, strictVersion: true, requiredVersion: 'auto' }, "@angular/common/http": { singleton: true, strictVersion: true, requiredVersion: 'auto' }, "@angular/router": { singleton: true, strictVersion: true, requiredVersion: 'auto' }, ...sharedMappings.getDescriptors() }) }), sharedMappings.getPlugin() ], };
shellApp 核心代码
app.routing.ts
import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; import {loadRemoteModule} from "@angular-architects/module-federation"; const routes: Routes = [ { path: 'mfeApp', loadChildren: () => loadRemoteModule({ remoteEntry: 'http://localhost:4201/remoteEntry.js', remoteName: 'mfeApp', exposedModule: './TestModule', }).then(m => m.TestModule) } ]; @NgModule({ imports: [RouterModule.forRoot(routes)], exports: [RouterModule] }) export class AppRoutingModule { }
webpack.config.ts
const ModuleFederationPlugin = require("webpack/lib/container/ModuleFederationPlugin"); const mf = require("@angular-architects/module-federation/webpack"); const path = require("path"); const share = mf.share; const sharedMappings = new mf.SharedMappings(); sharedMappings.register( path.join(__dirname, 'tsconfig.json'), [/* mapped paths to share */]); module.exports = { output: { uniqueName: "shellApp", publicPath: "auto" }, optimization: { runtimeChunk: false }, resolve: { alias: { ...sharedMappings.getAliases(), } }, experiments: { outputModule: true }, plugins: [ new ModuleFederationPlugin({ library: { type: "module" }, remotes: { "mfeApp": "http://localhost:4201/remoteEntry.js", }, shared: share({ "@angular/core": { singleton: true, strictVersion: true, requiredVersion: 'auto' }, "@angular/common": { singleton: true, strictVersion: true, requiredVersion: 'auto' }, "@angular/common/http": { singleton: true, strictVersion: true, requiredVersion: 'auto' }, "@angular/router": { singleton: true, strictVersion: true, requiredVersion: 'auto' }, ...sharedMappings.getDescriptors() }) }), sharedMappings.getPlugin() ], };
问题排查与解决方案
1. 修复mfeApp的TestModule路由配置
shellApp使用loadChildren加载的模块必须包含子路由配置,否则Angular路由无法识别该模块的路由结构,导致加载后无法初始化路由容器,抛出container is undefined错误。
修改test.module.ts,添加子路由配置:
import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { RouterModule, Routes } from '@angular/router'; import { AbcComponent } from './abc/abc.component'; // 配置TestModule的子路由,空路径对应shell的/mfeApp路径 const routes: Routes = [ { path: '', component: AbcComponent } ]; @NgModule({ declarations: [ AbcComponent ], imports: [ CommonModule, RouterModule.forChild(routes) // 使用forChild而非forRoot配置子模块路由 ], exports: [RouterModule] // 导出RouterModule让父路由系统识别 }) export class TestModule { }
2. 修正shellApp的Webpack Remotes配置
由于两个应用都开启了experiments.outputModule: true(ES模块格式),remotes的配置格式必须为"remoteName@remoteEntryUrl",否则无法正确加载远程模块。
修改shellApp的webpack.config.ts中remotes部分:
remotes: { "mfeApp": "mfeApp@http://localhost:4201/remoteEntry.js", },
3. 简化shellApp的路由加载配置(可选)
在webpack中配置好remotes后,loadRemoteModule可以省略remoteEntry参数,直接通过remoteName加载:
const routes: Routes = [ { path: 'mfeApp', loadChildren: () => loadRemoteModule({ type: 'module', remoteName: 'mfeApp', exposedModule: './TestModule', }).then(m => m.TestModule) } ];
额外检查点
- 确保两个应用的Angular版本完全一致,避免共享模块版本不匹配导致的加载失败
- 清空浏览器缓存,重启两个应用的开发服务器,确保Webpack配置生效
- 验证mfeApp的
publicPath配置,开发环境下auto是合理值,生产环境需根据部署路径调整
内容的提问来源于stack exchange,提问作者M Matthieu
相关产品推荐
相关产品推荐

