模块联邦核心概念与工作原理
模块联邦(Module Federation)是Webpack 5引入的微前端架构能力,允许在运行时从远程加载另一个Webpack构建产物中的模块,实现多个独立构建之间的代码共享。传统微前端方案如iframe、single-spa存在样式隔离、通信复杂、部署耦合等问题,模块联邦通过Webpack构建时插件机制,在编译期生成远程模块的运行时加载器,实现了真正的动态模块加载和依赖共享。模块联邦涉及两个角色:Host(消费方,引用远程模块的应用)和Remote(提供方,暴露模块的应用)。一个应用可同时作为Host和Remote,实现双向共享。
Remote端暴露模块配置
作为模块提供方,需在webpack.config.js中配置ModuleFederationPlugin,声明暴露的模块和共享依赖:
// remote-app/webpack.config.js
const ModuleFederationPlugin = require("webpack/lib/container/ModuleFederationPlugin");
const deps = require("./package.json").dependencies;
module.exports = {
output: {
publicPath: "http://localhost:3001/",
uniqueName: "remoteApp"
},
plugins: [
new ModuleFederationPlugin({
name: "remoteApp",
filename: "remoteEntry.js",
exposes: {
"./Button": "./src/components/Button",
"./Dashboard": "./src/pages/Dashboard",
"./utils": "./src/utils/shared"
},
shared: {
react: { singleton: true, requiredVersion: deps.react },
"react-dom": { singleton: true, requiredVersion: deps["react-dom"] },
"react-router-dom": { singleton: true, requiredVersion: deps["react-router-dom"] }
}
})
]
};
配置说明:name是Remote应用在运行时的全局变量标识,filename是远程入口文件名,exposes声明对外暴露的模块路径映射,shared配置共享依赖及其版本要求。singleton: true确保React等单例库只加载一份实例,避免Hooks失效等问题。
Host端加载远程模块配置
Host应用通过ModuleFederationPlugin声明要消费的Remote应用和共享依赖:
// host-app/webpack.config.js
const ModuleFederationPlugin = require("webpack/lib/container/ModuleFederationPlugin");
const deps = require("./package.json").dependencies;
module.exports = {
plugins: [
new ModuleFederationPlugin({
name: "hostApp",
remotes: {
remoteApp: "remoteApp@http://localhost:3001/remoteEntry.js",
analyticsApp: "analyticsApp@http://localhost:3002/remoteEntry.js"
},
shared: {
react: { singleton: true, requiredVersion: deps.react },
"react-dom": { singleton: true, requiredVersion: deps["react-dom"] }
}
})
]
};
在Host应用的React组件中,使用动态import加载远程模块:
// host-app/src/App.tsx
import React, { Suspense, lazy } from "react";
const RemoteButton = lazy(() => import("remoteApp/Button"));
const RemoteDashboard = lazy(() => import("remoteApp/Dashboard"));
function App() {
return (
<div>
<h1>Host Application</h1>
<Suspense fallback={<div>Loading...</div>}>
<RemoteButton onClick={() => console.log("clicked")} />
</Suspense>
<Suspense fallback={<div>Loading Dashboard...</div>}>
<RemoteDashboard />
</Suspense>
</div>
);
}
export default App;
React.lazy配合Suspense实现远程模块的懒加载,首次访问时才加载远程代码块,优化首屏性能。
共享依赖版本协调机制
共享依赖是模块联邦的关键特性,多个应用共享同一份依赖可减少重复加载和实例冲突。Webpack在运行时根据shared配置的requiredVersion协商使用哪个版本:若Host和Remote提供的版本都满足要求,优先使用Host的版本;若不满足则使用Remote自带的版本。singleton配置的依赖强制全局唯一实例。共享依赖初始化是异步的,Webpack 5.3+通过eager: true可改为同步初始化,但会增加首屏加载时间。
跨应用通信与状态共享方案
模块联邦本身不提供跨应用通信机制,需借助外部方案实现。常见做法包括:通过window对象挂载全局事件总线;使用共享的Redux Store(通过shared配置暴露store实例);基于CustomEvent的发布订阅模式。以下是一个基于事件总线的通信示例:
// shared/eventBus.ts(在shared配置中暴露)
type Handler = (payload?: any) => void;
const handlers = new Map<string, Set<Handler>>();
export const eventBus = {
on(event: string, handler: Handler) {
if (!handlers.has(event)) handlers.set(event, new Set());
handlers.get(event)!.add(handler);
},
emit(event: string, payload?: any) {
handlers.get(event)?.forEach(h => h(payload));
},
off(event: string, handler: Handler) {
handlers.get(event)?.delete(handler);
}
};
Host和Remote通过import(“remoteApp/eventBus”)获取同一实例,实现双向通信。
部署架构与生产环境注意事项
模块联邦的生产部署需注意:Remote应用的remoteEntry.js必须通过CDN或稳定URL提供,Host通过该URL加载远程模块;共享依赖的版本兼容性需提前规划,建议各应用使用相同的major版本;远程模块加载失败需有降级方案,通过Error Boundary捕获加载异常;生产环境建议启用Module Federation的TypeScript类型支持,使用@module-federation/typescript插件生成远程模块的.d.ts类型声明,保证开发体验。
原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/webpack5-mo-kuai-lian-bang-wei-qian-duan-jia-gou-da-jian-yu/