模块联邦(Module Federation)详解:从概念到手把手 Demo

模块联邦(Module Federation)详解:从概念到手把手 Demo

一文搞清 Webpack 5 的模块联邦是什么、能解决什么问题;并手把手用 Host + Remote 两个小项目跑通一个「远程组件」Demo,可直接按步骤复现。


一、模块联邦是什么

模块联邦(Module Federation)Webpack 5 内置的能力,让多个独立构建、独立部署 的前端应用,在运行时 像用本地模块一样去加载彼此的代码。也就是说:应用 A 不用把组件发到 NPM,应用 B 也不用先 npm install,只要在构建时配置好「谁暴露、谁消费」,运行时 B 就能动态拉取 A 暴露的模块并执行。

和常见方案的差异

  • NPM 发包:需要先发布、再安装、再构建,版本强耦合,发版节奏绑在一起。
  • iframe / 微前端子应用:隔离强但通信麻烦,样式、路由、性能都有一堆坑。
  • 模块联邦 :运行时按需拉取远程入口(如 remoteEntry.js),通过 exposes 暴露、remotes 消费,共享依赖(如 React)可配成单例,独立构建、独立部署,适合微前端、跨应用组件共享、多团队协作。

一句话:把「远程应用」当成一个「模块容器」,按需拉取其中暴露的模块,并和当前应用共享同一份依赖。


二、有什么用、典型场景

  • 微前端 :主应用(Host)集成多个子应用(Remote),子应用可独立开发、独立上线,主应用只负责拉取对应 remoteEntry.js 和路由。
  • 跨应用共享组件/工具 :设计系统、公共组件库以 Remote 形式部署,业务应用按需 import('designSystem/Button'),无需发 NPM 也能同步更新。
  • 多团队并行:各团队维护自己的 Remote,Host 通过 remotes 配置按环境指向不同地址,便于灰度与 A/B 测试。
  • 渐进式拆分:老项目逐步拆成多个 Remote,新功能用 Host 动态加载,无需一次性重构。

三、核心概念与官方链接

概念 说明
Remote(远程应用) 通过 ModuleFederationPluginexposes 暴露模块,打包出 remoteEntry.js,供别人加载。
Host(宿主应用) 通过 remotes 配置要用的 Remote 的入口 URL,运行时用 import('remoteName/ExposedModule') 消费。
shared 双方声明共享依赖(如 react、react-dom),可配 singleton: true 保证只加载一份,避免多实例冲突。

官方与社区


四、环境与准备

  • Node:建议 16+,确保能跑 Webpack 5。
  • 两个独立项目 :一个当 Remote (暴露组件),一个当 Host (消费远程组件)。下面用 React 18 + Webpack 5 举例,端口为 Remote 3002 、Host 3001

五、手把手 Demo

5.1 Remote 项目(端口 3002)

1. 初始化并安装依赖

bash 复制代码
mkdir remote-app && cd remote-app
npm init -y
npm i react react-dom
npm i -D webpack webpack-cli webpack-dev-server html-webpack-plugin babel-loader @babel/core @babel/preset-react

2. 暴露一个简单组件

src/Button.jsx

jsx 复制代码
import React from 'react';

export function Button({ children, onClick }) {
  return <button onClick={onClick}>{children}</button>;
}

3. 入口与 HTML

public/index.html 里有一个 <div id="root"></div> 即可。src/index.js 若需要本地预览 Remote,可写:

javascript 复制代码
import React from 'react';
import { createRoot } from 'react-dom/client';
import { Button } from './Button.jsx';
createRoot(document.getElementById('root')).render(<Button>Remote 本地预览</Button>);

否则只保留空入口也行,Host 只关心暴露的 ./Button。接着在项目根目录新建 webpack.config.js

javascript 复制代码
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { ModuleFederationPlugin } = require('webpack').container;

module.exports = {
  entry: './src/index.js',
  mode: 'development',
  devServer: { port: 3002, historyApiFallback: true },
  output: {
    publicPath: 'http://localhost:3002/',
    clean: true,
  },
  module: {
    rules: [
      { test: /\.jsx?$/, use: 'babel-loader', exclude: /node_modules/ },
    ],
  },
  plugins: [
    new HtmlWebpackPlugin({ template: './public/index.html' }),
    new ModuleFederationPlugin({
      name: 'remoteApp',
      filename: 'remoteEntry.js',
      exposes: {
        './Button': './src/Button.jsx',
      },
      shared: {
        react: { singleton: true, requiredVersion: '^18.0.0' },
        'react-dom': { singleton: true, requiredVersion: '^18.0.0' },
      },
    }),
  ],
};

4. 启动 Remote

bash 复制代码
npx webpack serve

浏览器访问 http://localhost:3002,确认能打开且控制台无报错;同时 http://localhost:3002/remoteEntry.js 可访问。


5.2 Host 项目(端口 3001)

1. 初始化并安装依赖

bash 复制代码
mkdir host-app && cd host-app
npm init -y
npm i react react-dom
npm i -D webpack webpack-cli webpack-dev-server html-webpack-plugin babel-loader @babel/core @babel/preset-react

2. 入口与消费远程组件

src/index.jsimport React from 'react'; import { createRoot } from 'react-dom/client'; import { App } from './App.jsx'; createRoot(document.getElementById('root')).render(<App />);
public/index.html:含 <div id="root"></div> 即可。

src/App.jsx

jsx 复制代码
import React, { Suspense, lazy } from 'react';

const RemoteButton = lazy(() => import('remoteApp/Button'));

export function App() {
  return (
    <div>
      <h1>Host 应用</h1>
      <Suspense fallback={<span>加载远程组件中...</span>}>
        <RemoteButton onClick={() => alert('来自 Remote')}>
          远程按钮
        </RemoteButton>
      </Suspense>
    </div>
  );
}

3. Babel 配置

在 Host 根目录建 .babelrc{ "presets": ["@babel/preset-react"] }(Remote 同样需要,以便编译 JSX)。

4. Host 的 webpack 配置

javascript 复制代码
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { ModuleFederationPlugin } = require('webpack').container;

module.exports = {
  entry: './src/index.js',
  mode: 'development',
  devServer: { port: 3001, historyApiFallback: true },
  output: { publicPath: 'http://localhost:3001/', clean: true },
  module: {
    rules: [
      { test: /\.jsx?$/, use: 'babel-loader', exclude: /node_modules/ },
    ],
  },
  plugins: [
    new HtmlWebpackPlugin({ template: './public/index.html' }),
    new ModuleFederationPlugin({
      name: 'hostApp',
      remotes: {
        remoteApp: 'remoteApp@http://localhost:3002/remoteEntry.js',
      },
      shared: {
        react: { singleton: true, requiredVersion: '^18.0.0' },
        'react-dom': { singleton: true, requiredVersion: '^18.0.0' },
      },
    }),
  ],
};

5. 启动 Host

先保持 Remote 在 3002 运行,再在 Host 目录执行:

bash 复制代码
npx webpack serve

访问 http://localhost:3001,页面应出现「Host 应用」和「远程按钮」;点击按钮能弹出「来自 Remote」,即说明 Host 已正确加载 Remote 暴露的 Button


六、关键点与注意点

  • publicPath :Remote 的 output.publicPath 必须能让 Host 正确拼出 chunk 的完整 URL(开发时用 http://localhost:3002/),否则运行时加载 chunk 会 404。
  • shared 与版本 :Host 和 Remote 的 shared 尽量一致,requiredVersion 与本地安装的版本兼容;singleton: true 保证 React 只加载一份,避免「Invalid hook call」等问题。
  • CORS :开发时 webpack-dev-server 默认允许跨域;若 Remote 是单独域名/端口,需保证 Remote 服务端允许 Host 的 origin。
  • 生产环境 :remotes 的 URL 改为线上地址(如 CDN),例如 remoteApp@https://cdn.example.com/remote-app/remoteEntry.js
  • 一个应用既可当 Host 也可当 Remote :同时配置 exposesremotes 即可。

七、总结

  • 模块联邦 是 Webpack 5 的「运行时模块共享」能力:Remote 通过 exposes 暴露模块并产出 remoteEntry.js,Host 通过 remotes 拉取并 import('remoteName/ExposedModule') 使用;shared 可配 React 等依赖为单例,避免重复加载与冲突。
  • 适合微前端、跨应用组件共享、多团队独立发布;与 NPM 发包、iframe 子应用相比,更灵活、可独立构建部署。
  • 手把手要点:Remote 先跑在 3002 并保证 remoteEntry.js 可访问 → Host 在 3001 配置 remotes 指向该 URL → 两边 shared 一致并启用 singleton → 用 React.lazy + Suspense 加载远程组件。

按上面步骤即可在本地跑通一个最小 Demo;再根据需要扩展更多 exposes、多个 Remote 或生产环境 URL。若对你有用,欢迎点赞、收藏或评论区交流你的使用场景。

相关推荐
ywf12151 小时前
前端的dist包放到后端springboot项目下一起打包
前端·spring boot·后端
恋猫de小郭1 小时前
2026,Android Compose 终于支持 Hot Reload 了,但是收费
android·前端·flutter
hpoenixf7 小时前
2026 年前端面试问什么
前端·面试
还是大剑师兰特7 小时前
Vue3 中的 defineExpose 完全指南
前端·javascript·vue.js
泯泷8 小时前
阶段一:从 0 看懂 JSVMP 架构,先在脑子里搭出一台最小 JSVM
前端·javascript·架构
mengchanmian8 小时前
前端node常用配置
前端
monsion9 小时前
OpenCode 学习指南
人工智能·vscode·架构
华洛9 小时前
利好打工人,openclaw不是企业提效工具,而是个人助理
前端·javascript·产品经理
无羡仙9 小时前
实测 Claude 多 Agent 开发:项目经理开局摸鱼,我成了救火队员
架构
xkxnq9 小时前
第六阶段:Vue生态高级整合与优化(第93天)Element Plus进阶:自定义主题(变量覆盖)+ 全局配置与组件按需加载优化
前端·javascript·vue.js