React+Vite微前端:Module Federation动态远程加载报错求助
在Vite+Module Federation中使用Federation Runtime动态加载远程模块报错的解决方案
你遇到的Failed to get remoteEntry exports错误,本质是Federation Runtime无法正确解析Vite生成的远程应用入口文件,以下是针对性的修复步骤:
1. 修正主机应用的Runtime初始化配置
init方法的name字段需设置为主机应用的名称(即Vite配置中的name: 'ShellApp'),而非远程应用名称;同时显式指定远程模块类型为module(Vite默认生成ES模块格式的remoteEntry):
import { init, loadRemote, registerRemotes } from "@module-federation/enhanced/runtime"; init({ name: "ShellApp", // 改为主机应用名称 remotes: [ { name: "remote", entry: "http://localhost:4174/remoteEntry.js", type: "module" // 新增指定模块类型 }, ], });
2. 调整动态加载逻辑,确保远程注册完成
将远程注册逻辑提前到useEffect中,避免未完成注册就加载模块:
const useRemote = (scope: string, module: string) => { useEffect(() => { // 注册所有需要动态加载的远程应用 registerRemotes([ { name: "remote", entry: "http://localhost:4174/remoteEntry.js", type: "module" }, { name: "remoteToolbox", entry: "http://localhost:4175/remoteEntry.js", type: "module" }, ]); }, []); const LazyComponent = lazy(async () => { return loadRemote<{ default: any }>(`${scope}/${module}`, { from: "runtime", }) as Promise<{ default: any }>; }); // 其余代码保持不变 };
3. 修复主机Vite配置的共享依赖
取消注释主机配置中的shared字段,确保React等依赖可以被正确共享,避免远程应用加载时出现依赖冲突:
federation({ name: 'ShellApp', remotes: { dummy: "dummy.js", }, exposes: {}, filename: 'remoteEntry.js', shared: ['react', 'react-dom', 'react-router-dom'], // 取消注释开启共享依赖 }),
4. 确保远程应用的build配置正确
远程应用需保持build.target: 'esnext',可额外关闭CSS代码拆分避免加载问题:
// 远程应用Vite配置 build: { target: 'esnext', cssCodeSplit: false, // 可选,避免CSS加载异常 },
5. 排查CORS与网络问题
开发环境下,在远程应用的Vite配置中添加跨域允许头,确保主机应用能正常访问远程入口文件:
server: { fs: { allow: ['.'], }, headers: { 'Access-Control-Allow-Origin': '*', // 生产环境需指定具体域名 }, },
额外优化:实现动态切换远程组件
当前代码中setSystem修改的状态未关联到LazyComponent,导致点击按钮无法切换组件,可调整逻辑如下:
const useRemote = () => { const [currentRemote, setCurrentRemote] = useState<{scope: string, module: string} | null>(null); useEffect(() => { registerRemotes([ { name: "remote", entry: "http://localhost:4174/remoteEntry.js", type: "module" }, { name: "remoteToolbox", entry: "http://localhost:4175/remoteEntry.js", type: "module" }, ]); }, []); const LazyComponent = currentRemote ? lazy(async () => { return loadRemote<{ default: any }>(`${currentRemote.scope}/${currentRemote.module}`, { from: "runtime", }) as Promise<{ default: any }>; }) : null; return { setApp2: () => setCurrentRemote({scope: "remote", module: "remote-menu"}), setApp3: () => setCurrentRemote({scope: "remoteToolbox", module: "remote-toolbox"}), LazyComponent }; }; // 使用方式调整 function Home() { const { setApp2, setApp3, LazyComponent } = useRemote(); return ( <ErrorBoundary> <div> <button onClick={setApp2}>Load App 2 Widget</button> <button onClick={setApp3}>Load App 3 Widget</button> {LazyComponent && ( <Suspense fallback={<div>Loading...</div>}> <LazyComponent /> </Suspense> )} </div> </ErrorBoundary> ); }
内容的提问来源于stack exchange,提问作者Laura Díaz
相关产品推荐
相关产品推荐

