如何基于对象数组通过dynamic-import-vars动态导入联邦远程组件?
解决Vite联邦模块动态导入远程组件的错误
问题场景
在Vite React项目中,通过@originjs/vite-plugin-federation导入远程组件时,出现Uncaught TypeError: Failed to resolve module specifier 'remoteHRApp/landingdashboard'错误。相关代码如下:
组件列表定义
export const components = [ { name: "HRLandingDashBoard", path: "landingdashboard" }, { name: "HRUpdateProfile", path: "updateprofile" }, { name: "HRenrollmentList", path: "enrolementlist" }, { name: "HRemployeExit", path: "employeeexit" }, { name: "HRemployeExitApproval", path: "employeeexitapproval" }, { name: "HRBulkEnrolement", path: "bulkenrolement" }, { name: "HRBulkEnrolementExit", path: "bulkenrolementexit" }, { name: "HRRevokeEmployeeExit", path: "revokeemployeeexit" }, { name: "HREmployeeList", path: "employeelist" }, { name: "HRUpdateProfileApproval", path: "updateprofileapproval" }, { name: "HREmployeeExitReport", path: "employeeexitreport" } ];
动态导入代码
import { components } from "./module"; const componentImports: any = {}; components.forEach(({ name, path }) => { componentImports[name] = lazy( () => import(/* @vite-ignore */ `remoteHRApp/${path}`) ); });
路由使用代码
<Route path="hr-landingdashboard" element={ <Suspense fallback={<div>Loading...</div>}> <componentImports.HRLandingDashBoard /> </Suspense> } ></Route>
本地Vite配置
import { defineConfig } from "vite"; import react from "@vitejs/plugin-react-swc"; import federation from "@originjs/vite-plugin-federation"; import dynamicImportVars from "@rollup/plugin-dynamic-import-vars"; export default defineConfig({ base: "/", plugins: [ react(), federation({ name: "app", remotes: { remoteHRApp: "http://localhost:5051/hr-management/assets/remote-hr.js", }, shared: ["react", "react-dom"], }), dynamicImportVars({ include: ["src/**/*.tsx"], exclude: ["node_modules/**"], errorWhenNoFilesFound: true, warnOnError: true, }), ], build: { modulePreload: false, target: "esnext", minify: false, cssCodeSplit: false, }, });
解决方案
1. 修正远程组件导入逻辑
联邦模块的动态导入不能直接用模板字符串拼接,需改用远程模块的官方加载方式:
方法一:预加载远程模块
在项目入口文件(如main.tsx)中预先加载远程模块,确保全局对象可用:
import { loadRemoteModule } from '@originjs/vite-plugin-federation/src/utils'; import React from 'react'; import ReactDOM from 'react-dom/client'; async function bootstrap() { // 预加载远程HR模块 await loadRemoteModule({ remoteName: 'remoteHRApp', url: 'http://localhost:5051/hr-management/assets/remote-hr.js', }); // 加载App组件 const { default: App } = await import('./App'); ReactDOM.createRoot(document.getElementById('root')!).render( <React.StrictMode> <App /> </React.StrictMode>, ); } bootstrap();
然后在App.tsx中通过全局对象获取组件:
import { lazy } from 'react'; import { components } from './module'; const componentImports = components.reduce((acc, { name, path }) => { acc[name] = lazy(() => window.remoteHRApp.get(`./${path}`).then(module => ({ default: module.default })) ); return acc; }, {} as Record<string, React.LazyExoticComponent<any>>);
方法二:显式导入组件(适合组件数量固定场景)
放弃动态拼接,直接显式导入每个远程组件:
import { lazy } from 'react'; const componentImports = { HRLandingDashBoard: lazy(() => import('remoteHRApp/landingdashboard')), HRUpdateProfile: lazy(() => import('remoteHRApp/updateprofile')), // 其他组件依次添加 };
2. 修正JSX组件使用方式
React要求组件名必须以大写字母开头,直接使用对象属性访问会被识别为HTML元素,需改为以下两种方式之一:
方式一:提取为大写变量
import { Routes, Route } from 'react-router-dom'; import { Suspense } from 'react'; // 提取组件变量 const HRLandingDashBoard = componentImports.HRLandingDashBoard; function App() { return ( <Routes> <Route path="hr-landingdashboard" element={ <Suspense fallback={<div>Loading...</div>}> <HRLandingDashBoard /> </Suspense> } /> {/* 其他路由同理 */} </Routes> ); } export default App;
方式二:使用React.createElement
import { Routes, Route } from 'react-router-dom'; import { Suspense, createElement } from 'react'; function App() { return ( <Routes> <Route path="hr-landingdashboard" element={ <Suspense fallback={<div>Loading...</div>}> {createElement(componentImports.HRLandingDashBoard)} </Suspense> } /> {/* 其他路由同理 */} </Routes> ); } export default App;
3. 验证远程项目联邦配置
确保远程项目的vite.config.ts正确暴露所有组件:
// 远程项目vite.config.ts import { defineConfig } from 'vite'; import react from '@vitejs/plugin-react-swc'; import federation from '@originjs/vite-plugin-federation'; export default defineConfig({ plugins: [ react(), federation({ name: 'remoteHRApp', filename: 'remote-hr.js', exposes: { './landingdashboard': './src/components/HRLandingDashBoard.tsx', './updateprofile': './src/components/HRUpdateProfile.tsx', './enrolementlist': './src/components/HRenrollmentList.tsx', // 其他组件依次添加 }, shared: ['react', 'react-dom'], }), ], build: { modulePreload: false, target: 'esnext', minify: false, cssCodeSplit: false, }, });
4. 移除不必要的插件
@rollup/plugin-dynamic-import-vars是用于本地文件的动态变量导入,会干扰远程模块解析,建议从本地Vite配置中移除:
// 修改后的本地vite.config.ts import { defineConfig } from "vite"; import react from "@vitejs/plugin-react-swc"; import federation from "@originjs/vite-plugin-federation"; export default defineConfig({ base: "/", plugins: [ react(), federation({ name: "app", remotes: { remoteHRApp: "http://localhost:5051/hr-management/assets/remote-hr.js", }, shared: ["react", "react-dom"], }), ], build: { modulePreload: false, target: "esnext", minify: false, cssCodeSplit: false, }, });
内容的提问来源于stack exchange,提问作者Anandhu Remanan
相关产品推荐
相关产品推荐

