Webpack5 ModuleFederation多React远程子应用仅可加载一个故障咨询
问题根因
你遇到的问题是Module Federation多子应用接入的典型配置疏漏,核心是共享依赖配置不统一、资源路径配置错误、初始化逻辑重复触发三个原因导致。
修复方案
1. 修正子应用publicPath配置
Webpack要求绝对路径格式的publicPath必须以/结尾,否则子应用加载内部chunk时会出现路径拼接错误,这是你第二个子应用无法加载资源的高频诱因。
- 子应用1修改为:
publicPath: "http://localhost:3001/app1/", - 子应用2修改为:
publicPath: "http://localhost:3002/app2/",
2. 统一共享依赖配置,强制单例
父应用的react、react-dom已经配置了singleton: true强制单例,但子应用用数组形式声明shared默认不会开启单例、也不会做版本校验,第一个子应用初始化后共享域被注入对应版本的依赖,第二个子应用初始化时因为配置不匹配会卡住。
两个子应用的shared配置修改为和父应用对齐:
// 子应用1和子应用2的shared配置 shared: { react: { requiredVersion: require('./package.json').dependencies.react, singleton: true, }, "react-dom": { requiredVersion: require('./package.json').dependencies["react-dom"], singleton: true, }, "react-apollo": { singleton: true, } }
请确保三个应用的react、react-dom版本一致,避免版本不兼容报错。
3. 修复组件加载逻辑,避免重复初始化
当前loadComponent函数每次调用都会执行共享域初始化、容器初始化,而这两个操作都只能执行一次,重复调用会抛出未捕获错误导致组件加载中断。修改加载逻辑增加缓存标记:
// 缓存已初始化的容器和共享域状态 const initializedContainers = new Set(); let shareScopeInitialized = false; function loadComponent(scope, module) { return async () => { // 共享域全局仅初始化一次 if (!shareScopeInitialized) { await __webpack_init_sharing__("default"); shareScopeInitialized = true; } const container = window[scope]; if (!container) throw new Error(`远程容器${scope}未加载`); // 每个容器仅初始化一次 if (!initializedContainers.has(scope)) { await container.init(__webpack_share_scopes__.default); initializedContainers.add(scope); } const factory = await container.get(module); return factory(); }; }
可选优化:按需加载远程入口
你当前在html头部同步加载了两个子应用的远程入口,既浪费带宽也有初始化冲突风险,可以改成动态按需加载,修改逻辑如下:
const initializedContainers = new Set(); const loadedRemoteEntries = new Set(); let shareScopeInitialized = false; // 动态加载远程入口脚本 function loadRemoteEntry(scope, entryUrl) { return new Promise((resolve, reject) => { if (loadedRemoteEntries.has(scope)) return resolve(); const script = document.createElement('script'); script.src = entryUrl; script.onload = () => { loadedRemoteEntries.add(scope); resolve(); }; script.onerror = reject; document.head.appendChild(script); }); } function loadComponent(scope, module, entryUrl) { return async () => { await loadRemoteEntry(scope, entryUrl); if (!shareScopeInitialized) { await __webpack_init_sharing__("default"); shareScopeInitialized = true; } const container = window[scope]; if (!initializedContainers.has(scope)) { await container.init(__webpack_share_scopes__.default); initializedContainers.add(scope); } const factory = await container.get(module); return factory(); }; } // 组件声明修改为: const ComponentApp1 = React.lazy( loadComponent('app1', './App', 'http://localhost:3001/app1/remoteEntry.js') ); const ComponentApp2 = React.lazy( loadComponent('app2', './App', 'http://localhost:3002/app2/remoteEntry.js') );
修改后可以删掉index.html头部引入的两个远程入口script标签。
内容的提问来源于stack exchange,提问作者Yash Pahuja
相关产品推荐
相关产品推荐

