Angular 14多仓库Module Federation:远程MFE加载后不显示内容
问题描述
使用Angular v14.1.1搭建主机应用(host app)和远程应用(remote app)两个仓库,配置@angular-architects/module-federation插件后,远程应用的remoteEntry.js已在网络请求中加载,但远程MFE的内容无法在主机应用UI中显示。
主机运行地址:http://localhost:4200,远程应用运行地址:http://localhost:4201,访问http://localhost:4200/remotemfe1时,仅显示主机应用内容,无远程应用内容。
远程应用配置
webpack.config.js
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.base.json'), [/* mapped paths to share */]); module.exports = { output: { uniqueName: "remotemfe1", publicPath: "auto" }, optimization: { runtimeChunk: false }, resolve: { alias: { ...sharedMappings.getAliases(), } }, experiments: { outputModule: true }, plugins: [ new ModuleFederationPlugin({ library: { type: "module" }, // For remotes (please adjust) name: "remotemfe1", filename: "remoteEntry.js", exposes: { './Module': './apps/remotemfe1/src/app/app.module.ts', }, // For hosts (please adjust) // remotes: { // "mfe1": "http://localhost:3000/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() ], };
app.module.ts
import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { TestComponent } from './app.component'; @NgModule({ declarations: [TestComponent], imports: [ BrowserModule ], providers: [], bootstrap: [TestComponent], }) export class AppModule {}
主机应用配置
webpack.config.js
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.base.json'), [/* mapped paths to share */]); module.exports = { output: { uniqueName: "hostmf", publicPath: "auto" }, optimization: { runtimeChunk: false }, resolve: { alias: { ...sharedMappings.getAliases(), } }, experiments: { outputModule: true }, plugins: [ new ModuleFederationPlugin({ library: { type: "module" }, // For remotes (please adjust) // name: "hostmf", // filename: "remoteEntry.js", // exposes: { // './Component': './apps/hostmf/src/app/app.component.ts', // }, // For hosts (please adjust) // remotes: { // "remotemfe1": "http://localhost:3000/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() ], };
app.module.ts
import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { AppComponent } from './app.component'; import { RouterModule } from '@angular/router'; import { loadRemoteModule } from '@angular-architects/module-federation'; @NgModule({ declarations: [AppComponent], imports: [ BrowserModule, RouterModule.forRoot([ { path: 'remotemfe1', loadChildren: () => loadRemoteModule({ type: 'module', remoteEntry: 'http://localhost:4201/remoteEntry.js', exposedModule: './Module' }) .then(m => { console.log(m); return m.AppModule; }) } ]), ], providers: [], bootstrap: [AppComponent], }) export class AppModule {}
app.component.ts
import { Component } from '@angular/core'; @Component({ selector: 'hostrepo-root', templateUrl: './app.component.html', styleUrls: ['./app.component.scss'], }) export class AppComponent { title = 'hostmf'; }
app.component.html
host app works! <router-outlet></router-outlet>
index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <title>Hostmf</title> <base href="/" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <link rel="icon" type="image/x-icon" href="favicon.ico" /> </head> <body> <hostrepo-root></hostrepo-root> </body> </html>
问题分析与解决方案
核心问题1:远程模块缺少路由配置
远程应用的AppModule没有配置路由,导致加载后无法将TestComponent渲染到主机的router-outlet中。当主机通过loadChildren加载远程模块时,Angular需要该模块包含路由配置来匹配路径并渲染组件。
修复步骤:
- 在远程应用中添加
RouterModule,配置默认路由指向TestComponent
修改远程应用的app.module.ts:
import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { RouterModule, Routes } from '@angular/router'; // 引入路由模块 import { TestComponent } from './app.component'; // 配置路由 const routes: Routes = [ { path: '', component: TestComponent } ]; @NgModule({ declarations: [TestComponent], imports: [ BrowserModule, RouterModule.forChild(routes) // 使用forChild配置子路由 ], providers: [] // 移除bootstrap配置:远程模块不需要独立启动,由主机控制渲染 }) export class AppModule {}
核心问题2:主机加载逻辑简化(可选)
当前loadChildren写法可正常工作,但可以简化为更简洁的形式:
修改主机应用app.module.ts中的路由配置:
RouterModule.forRoot([ { path: 'remotemfe1', loadChildren: () => loadRemoteModule({ type: 'module', remoteEntry: 'http://localhost:4201/remoteEntry.js', exposedModule: './Module' }).then(m => m.AppModule) } ])
额外检查项
- 确认远程应用的
TestComponent模板有实际内容,比如在app.component.html中添加<h1>Remote MFE works!</h1> - 检查浏览器控制台:查看是否有模块加载、依赖共享相关错误(如版本不匹配、依赖冲突)
- 确认CORS配置:开发模式下Angular CLI默认允许跨域,若有自定义服务器配置需检查是否允许主机请求
验证步骤
- 重启主机和远程应用
- 访问
http://localhost:4200/remotemfe1,此时应同时显示主机和远程应用的内容
内容的提问来源于stack exchange,提问作者cj devin
相关产品推荐
相关产品推荐

