Webpack Module Federation远程不可用时宿主数秒空白页如何解决
直接写在ModuleFederationPlugin.remotes字段的静态远程应用,默认会被Webpack标记为初始阻塞依赖:Webpack启动阶段会优先拉取所有远程应用的remoteEntry.js,等待所有初始依赖加载完成(无论成功/失败)后,才会执行入口代码,哪怕你用了动态import('./bootstrap.jsx')做引导拆分,bootstrap.jsx的执行依然会被remote加载阻塞,这就是白屏问题的核心来源。
你在host侧给react、react-dom配置了eager: true,仅会把共享依赖打包进host初始bundle,省去共享依赖的异步拉取流程,并没有改变静态remote默认初始加载的逻辑,因此阻塞问题不会消失。
Webpack Module Federation原生支持Promise形式的remote声明,直接写在remotes配置项中即可实现非阻塞加载,不需要额外手写动态注入容器的逻辑,修改host侧配置如下:
new ModuleFederationPlugin({ name: "host", remotes: { // 替换原来的静态地址为promise前缀的异步声明 remote1: `promise new Promise((resolve, reject) => { // 自定义加载超时时间,避免长时间等待 const loadTimeout = setTimeout(() => reject(new Error('Remote1 load timeout')), 3000) const remoteScript = document.createElement('script') remoteScript.src = 'http://localhost:8081/remoteEntry.js' remoteScript.onload = () => { clearTimeout(loadTimeout) // 加载完成后向Webpack暴露remote容器实例 resolve({ get: (exportPath) => window.remote1.get(exportPath), init: (shareScope) => { if (!window.remote1.__initialized) { window.remote1.__initialized = true window.remote1.init(shareScope) } } }) } remoteScript.onerror = (err) => { clearTimeout(loadTimeout) reject(err) } document.head.appendChild(remoteScript) })` }, shared: { "react": { singleton: true, // eager配置不影响非阻塞效果:希望host核心依赖初始化更快可设为true,需要更优初始包体积则设为false eager: false, requiredVersion: deps["react"] }, "react-dom": { singleton: true, eager: false, requiredVersion: deps["react-dom"] }, } })
remote1侧的配置不需要做任何修改,保持原有写法即可。
配置修改后,host初始渲染不会再等待remote加载,你只需要在引用远程组件的位置配合懒加载和错误边界,即可实现自定义加载态和错误兜底:
import React, { Suspense, lazy } from 'react' import Spinner from './components/Spinner' import ErrorBoundary from './components/ErrorBoundary' // 远程组件懒加载,仅当组件被渲染时才会触发remoteEntry拉取 const RemoteRegister = lazy(() => import('remote1/register')) function App() { return ( <div className="host-app"> {/* host侧自有内容会立即渲染,不受remote加载影响 */} <header>Host 主应用导航栏</header> <main> <ErrorBoundary fallback={<div className="error-tip">远程模块加载失败,请刷新重试</div>}> <Suspense fallback={<Spinner />}> <RemoteRegister /> </Suspense> </ErrorBoundary> </main> </div> ) } export default App
当remote配置值以promise 为前缀时,Webpack会将其识别为异步远程容器,不会把它加入初始阻塞依赖列表,只有当代码中真正引用该remote的导出模块时,才会执行配置中的Promise逻辑加载远程脚本。这样bootstrap.jsx的渲染逻辑会在host初始资源加载完成后立即执行,你可以先渲染主应用框架和自定义加载组件,完全规避白屏阻塞问题。
内容的提问来源于stack exchange,提问作者Patrick Lafrance

