module federation 避免 sharedmoduleerror 需统一 shared 配置(singleton + requiredversion)、禁止插件打包 shared 包;版本更新依赖带版本号的 remoteentry url 与 shared 缓存清理;hmr 需导出 mount/unmount/update 并由主应用调度;插件通信须通过主应用注册表或事件总线,禁用构建期跨插件 import。

Module Federation 插件如何避免 SharedModuleError 导致的加载失败
联邦插件系统一旦涉及多版本共存,SharedModuleError: Shared module is not available for eager consumption 就高频出现。根本原因不是配置漏写,而是 shared 中对同一包(如 react、lodash)的版本约束冲突:主应用声明 react@18.2.0 为 singleton,而插件 A 打包时锁定了 react@18.3.1 并试图“强共享”,Federation 运行时直接拒绝加载。
实操上必须统一“谁提供、谁消费、是否兼容”三要素:
-
shared配置中对基础库(react、react-dom、vue等)必须显式指定singleton: true和requiredVersion(如^18.2.0),不能留空或写true - 所有插件的
webpack.config.js中shared必须与主应用完全一致——不是“相似”,是JSON.stringify级别一致;建议抽成独立 config 文件供主应用和插件共同 import - 插件构建时禁止将 shared 包打进自身 bundle:
externals或exposes周边不引入node_modules/react类路径
插件版本号如何被运行时识别并实现增量更新
Module Federation 本身不解析语义化版本,它只认 remoteEntry.js 的 URL。所谓“版本增量更新”,本质是让主应用能按需加载不同 URL 的远程模块,且不破坏已有实例状态。
关键在远程容器注册策略和模块缓存控制:
- 主应用用动态
import()加载插件时,URL 必须含明确版本标识,例如:import('https://cdn.example.com/plugins/chart/v2.4.1/remoteEntry.js'),而非固定写死v2/remoteEntry.js - 每次加载新版本前,调用
__webpack_init_sharing__('default')清除旧 shared 缓存(否则老版本 react 可能被复用) - 插件暴露的模块需封装一层 factory 函数,避免导出对象被 webpack 模块缓存污染,例如:
exposes: { './Chart': './src/Chart.tsx' }应改为:exposes: { './Chart': './src/ChartFactory.ts' },其中ChartFactory.ts返回一个创建新实例的函数
如何让插件热替换(HMR)不触发整个页面重载
Federation 默认 HMR 会触发 container.unmount() + container.mount(),如果插件内有全局副作用(如监听 window.resize、挂载第三方 SDK),重载后状态丢失或重复初始化。
绕过默认行为需要接管容器生命周期:
- 插件入口文件(如
bootstrap.ts)不直接执行渲染,而是导出mount、unmount、update三个函数,由主应用统一调度 - 主应用在
hot.accept回调中调用插件的update(),该函数只更新组件 props 或重新触发局部 render,不销毁 DOM 根节点 - 禁用 Federation 内置 HMR:在插件 webpack config 中设置
devServer.hot: false,改用主应用侧的hot实例管理所有联邦模块
插件间通信为何不能直接 import 对方 expose 的模块
两个插件(A 和 B)都通过 Module Federation 暴露模块,但 A 在代码里写 import { utils } from 'plugin-b/utils' 会报 Module not found —— 因为 Webpack 构建期无法解析运行时才注册的 remote。
跨插件调用必须走运行时契约:
- 主应用作为“中央注册表”,在初始化阶段收集各插件暴露的 capability,例如:
window.$plugins = { chart: { render: ... }, auth: { login: ... } } - 插件内部通过
window.$plugins查找依赖插件,而不是静态 import;或使用发布-订阅模式,主应用转发事件 - 若必须类型安全,可定义统一
@types/plugins包,所有插件依赖它,但具体实现仍靠运行时注入
最易被忽略的是:插件构建产物中不能包含对其他插件的 import 语句,哪怕加了 /* webpackIgnore: true */ 也不行——Webpack 5+ 仍会尝试解析,导致 build 失败。










