You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何基于对象数组通过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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 09:35:00