微前端MFE: 通过共享模块通信(模块联邦Module Federation)
背景
传统微前端中,不同子应用彼此完全独立,通信主要通过事件、消息总线等方式。随着模块联邦(Module Federation)技术的发展,可以在多个微前端应用间动态共享模块,实现模块级别的调用和通信,打破边界。
核心思想
-
利用 Webpack Module Federation 机制,多个应用间共享和暴露模块(函数、组件、状态管理实例等)。
-
通过导入其他应用暴露的模块,直接调用对方的功能或状态,达成通信目的。
具体实现步骤
1. 应用间暴露接口模块
remote 应用通过 Module Federation 配置,暴露一个模块,比如状态管理实例、工具函数或事件总线。
// webpack.config.js (remote 应用)
module.exports = {// ...plugins: [new ModuleFederationPlugin({name: 'remoteApp',filename: 'remoteEntry.js',exposes: {'./EventBus': './src/eventBus.js', // 暴露一个事件总线模块'./sharedState': './src/sharedState.js' // 也可以暴露状态实例},shared: ['react', 'react-dom'], // 共享依赖}),],
};
2. host 应用动态加载并调用 remote 模块
在主应用或其他子应用里,动态加载这个暴露的模块,并使用它完成通信。
// host 应用中,动态加载 remote 应用暴露的模块
import('remoteApp/EventBus').then(eventBusModule => {const eventBus = eventBusModule.default;// 订阅事件eventBus.on('some-event', (data) => {console.log('收到 remote 应用消息:', data);});// 触发事件,通知 remote 应用eventBus.emit('host-event', { msg: 'Hello from host' });
});
共享状态的例子
比如两个应用共享同一个 sharedState
实例(可以是 Redux store、MobX store 或自定义状态管理)。
// remote 应用 sharedState.js
import { makeAutoObservable } from 'mobx';class SharedState {data = 0;constructor() {makeAutoObservable(this);}setData(value) {this.data = value;}
}const sharedState = new SharedState();
export default sharedState;
host 应用调用:
import('remoteApp/sharedState').then(sharedState => {console.log(sharedState.data);sharedState.setData(42);
});
完整示例
基于 Module Federation 共享事件总线 ,演示 remote-app
如何在组件内监听 host-to-remote
事件,并展示消息。
1. remote-app 结构和代码
假设前端框架用的是 React,remote-app 目录结构:
remote-app/src/eventBus.jsApp.jsxindex.jswebpack.config.jspackage.json
1.1 webpack.config.js
const { ModuleFederationPlugin } = require('webpack').container;
const path = require('path');module.exports = {mode: 'development',entry: './src/index.js',devServer: {port: 3001,historyApiFallback: true,},output: {publicPath: 'auto',path: path.resolve(__dirname, 'dist'),},module: {rules: [{test: /\.jsx?$/,loader: 'babel-loader',exclude: /node_modules/,},],},resolve: {extensions: ['.js', '.jsx'],},plugins: [new ModuleFederationPlugin({name: 'remoteApp',filename: 'remoteEntry.js',exposes: {'./eventBus': './src/eventBus.js',},shared: ['react', 'react-dom', 'mitt'],}),],
};
1.2 src/eventBus.js
import mitt from 'mitt';const eventBus = mitt();export default eventBus;
1.3 src/App.jsx
import React, { useEffect, useState } from 'react';
import eventBus from './eventBus';export default function App() {const [message, setMessage] = useState('');useEffect(() => {// 监听 host 应用发来的事件const handler = (data) => {setMessage(data.msg);console.log('remote-app 收到 host 发送的消息:', data);};eventBus.on('host-to-remote', handler);// 清理订阅return () => {eventBus.off('host-to-remote', handler);};}, []);// 远程主动发送消息给 hostuseEffect(() => {setTimeout(() => {eventBus.emit('remote-to-host', { msg: 'Hello from remote-app React component!' });}, 3000);}, []);return (<div><h2>Remote App (React)</h2><p>收到 host 的消息: {message || '暂无消息'}</p></div>);
}
1.4 src/index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);
2. host-app 结构和代码
host-app 目录结构类似:
host-app/src/index.jswebpack.config.jspackage.json
2.1 webpack.config.js
const { ModuleFederationPlugin } = require('webpack').container;
const path = require('path');module.exports = {mode: 'development',entry: './src/index.js',devServer: {port: 3000,historyApiFallback: true,},output: {publicPath: 'auto',path: path.resolve(__dirname, 'dist'),},plugins: [new ModuleFederationPlugin({name: 'hostApp',remotes: {remoteApp: 'remoteApp@http://localhost:3001/remoteEntry.js',},shared: ['mitt'],}),],
};
2.2 src/index.js
// host 应用中动态加载 remote-app 暴露的事件总线
import('remoteApp/eventBus').then((module) => {const eventBus = module.default;// 监听 remote 发来的消息eventBus.on('remote-to-host', (data) => {console.log('host-app 收到 remote 发送的消息:', data);});// 发送消息给 remote 应用setTimeout(() => {eventBus.emit('host-to-remote', { msg: 'Hi from host-app after 5 seconds!' });}, 5000);
});
3. 启动顺序
-
启动 remote-app(端口3001)
-
启动 host-app(端口3000)
-
host-app 会动态加载 remote-app 的事件总线模块
-
remote-app React 组件会监听
host-to-remote
事件,5秒后 host-app 发送消息,remote-app 显示消息 -
remote-app 3秒后发消息给 host-app,host-app 控制台打印消息
优点
-
模块级别通信,直接调用函数、操作状态,不必通过事件转发或序列化。
-
代码复用性强,减少重复实现。
-
易于维护,接口清晰。
-
支持多种通信模式(事件总线、状态管理、工具函数等)。
缺点/挑战
-
依赖构建配置复杂,需统一 webpack Module Federation 配置。
-
应用间耦合度稍高,不适合完全独立解耦的场景。
-
需要解决共享依赖版本冲突问题(React、ReactDOM 等)。
-
只能用于同一浏览器上下文内(单页面应用或 iframe 内嵌需额外配置)。
适用场景
-
多个微前端应用间需要共享状态或业务逻辑。
-
需要高度复用组件或工具库。
-
使用 Module Federation 技术栈。
-
微前端加载在同一页面,且能共享同一个 JS 运行时环境。