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

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需要该模块包含路由配置来匹配路径并渲染组件。

修复步骤:

  1. 在远程应用中添加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默认允许跨域,若有自定义服务器配置需检查是否允许主机请求

验证步骤

  1. 重启主机和远程应用
  2. 访问http://localhost:4200/remotemfe1,此时应同时显示主机和远程应用的内容

内容的提问来源于stack exchange,提问作者cj devin

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.23 11:18:28