You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 01:05:06