@fulgurjs/federation
v5.9.3
Published
Vite Module Federation for Vue and React: remote modules, shared dependencies and app bridges.
Maintainers
Readme
@fulgurjs/federation
所有模板与 Demo 统一入口:示例选择与运行指南。
让一个 Vite 应用使用另一个应用提供的组件、页面或函数。
例如:主系统加载独立部署的审批页面,Vue 页面中嵌入一个 React 子应用,或者多个应用共用同一套工具函数。提供方和使用方可以放在不同仓库,各自构建和部署。
本文是使用指南,示例与模板按 5.9.0 编写(各工程 package.json 声明的精确版本即实际安装版本)。完整参数、默认值和执行规则在 API 手册。
先看你要做什么
| 你的需求 | 使用方法 | 示例 |
|---|---|---|
| Vue 加载另一个应用的 Vue 组件 | remoteComponent | Vue 示例 |
| React 加载另一个应用的 React 组件 | /react 的 remoteComponent | React 示例 |
| 加载远程 JS/TS 函数 | loadRemote;React 也可用 useLoadRemote | 见下方快速开始 |
| 一批宿主路由对应远程页面 | Vue 用 createHostPages;React 用 createReactHostPages | 页面接入示例 |
| Vue 嵌 React,或 React 嵌 Vue | 子应用桥接:defineBridgeApp + 宿主桥接组件 | 双向嵌套示例 |
| 刷新后仍打开子应用的详情页 | 在桥接上开启 URL 同步 | 路由同步示例 |
| 远程页面需要用户、token 或初始化 | AppContext + 可选 setup/onSession | 见下方业务初始化 |
| 同页使用 React 18 和 React 19 | 将两组依赖和使用方放入不同 shareScope | 版本隔离示例 |
这些功能按需组合。加载一个普通组件,不需要先配置桥接、页面表或登录初始化。
几个名字是什么意思
| 名字 | 用普通话解释 |
|---|---|
| 宿主(host) | 显示远程内容的应用,例如主系统 |
| 远程(remote) | 提供组件、页面或函数的应用,例如审批系统 |
| exposes | 远程允许其他应用加载哪些文件 |
| remotes | 宿主要连接哪些远程、它们的地址是什么 |
| shared | 哪些依赖参与共享,例如 Vue、React |
| singleton | 同一个共享分组内只采用一个依赖实例;不保证所有库都能跨大版本兼容 |
| shareScope | 共享依赖的分组。不同组可以使用不同版本 |
| 桥接 | 为子应用准备一个 DOM 容器,让它在里面自行渲染和卸载 |
| URL 同步 | 把子应用的内部路径写进宿主地址,支持刷新、分享和前进后退 |
同一个应用可以既提供模块又使用其他应用的模块,不需要固定为单一角色。
安装
在每个参与联邦的 Vite 项目中安装:
pnpm add -D @fulgurjs/federation
# 使用 npm 的项目:npm install -D @fulgurjs/federation- 支持浏览器端 Vue 3、React 18/19,以及普通 JS/TS 模块。
- 支持 Vite 5.1 及以上的 5/6/7/8 系列;项目必须同时满足所用 Vite 和框架插件的版本要求。
- 本插件要求 Node.js ≥18,但 Vite 7/8 要求 Node.js 20.19+ 或 22.12+,不能只按插件的最低版本选 Node。
- 构建目标使用
es2022或更新;浏览器基线为 Chrome 108+,其他浏览器需要相应的 ESM、动态导入和顶层 await 支持。 - 普通 Vue 项目安装 Vue 即可;普通 React 项目安装 React 和 react-dom 即可。双向跨框架桥接的宿主按下面说明安装两个框架。
从零开始:创建一个完整工程
没有可接入的存量项目时,用 CLI 从完整模板创建(需要 Node ≥ 20 和 pnpm ≥ 9):
npx @fulgurjs/federation create # 交互选择场景;也可显式指定:
npx @fulgurjs/federation create vue-vue --dir my-federation五个模板覆盖:Vue×Vue、React×React、Vue 宿主嵌 React 子应用、React 宿主嵌 Vue 子应用、双向桥接+URL 同步 showcase。create 复制完整可运行的 workspace(含锁文件与启动脚本)并默认执行冻结安装,完成后打印进入目录、启动和构建命令。要接入已有项目时跳过这步,用下面的快速开始 + fulgurjs init。
模板源码在仓库 examples/templates/;不改名称/端口时无需任何手工配置。改端口的固定修改点见模板指南。
快速开始:两个 Vue 应用
下面是在已有 Vite + Vue 项目中增加联邦功能。每个项目仍保留自己的 index.html、入口文件和原有插件。
我们使用两个项目:
remote-vue/ 提供按钮和加法函数,开发端口 5174
host-vue/ 加载它们,开发端口 51731. 远程声明要提供的文件
remote-vue/fulgurjs.config.ts:
import type { FederationOptions } from '@fulgurjs/federation'
export default {
name: 'remote-vue',
exposes: {
'./Button': './src/Button.vue',
'./math': './src/math.ts',
},
shared: { vue: { singleton: true, strictVersion: true } },
} satisfies FederationOptionsremote-vue/src/Button.vue:
<script setup lang="ts">
import { ref } from 'vue'
defineProps<{ label: string }>()
const count = ref(0)
</script>
<template>
<button @click="count++">{{ label }}:{{ count }}</button>
</template>remote-vue/src/math.ts:
export function add(a: number, b: number): number {
return a + b
}2. 宿主声明远程地址
host-vue/fulgurjs.config.ts:
import type { FederationOptions } from '@fulgurjs/federation'
export default {
name: 'host-vue',
remotes: {
'remote-vue': {
dev: 'http://localhost:5174',
prod: '/remote-vue',
},
},
shared: { vue: { singleton: true, strictVersion: true } },
} satisfies FederationOptionsdev 是开发地址,prod 是部署后的地址。这里的 /remote-vue 表示宿主所在域名下的远程目录,不是磁盘文件夹路径。
3. 两个项目都注册插件
两个项目的 vite.config.ts 都导入本项目的配置:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import federation from '@fulgurjs/federation'
import fulgurjsConfig from './fulgurjs.config'
export default defineConfig({
plugins: [vue(), federation(fulgurjsConfig)],
build: { target: 'es2022' },
})保留项目已有的别名、代理等配置。远程与宿主应安装兼容的 Vue 版本;示例开启 strictVersion,不兼容时会报错,而不是继续使用错误版本。
4. 在宿主页中使用
host-vue/src/App.vue:
<script setup lang="ts">
import { ref } from 'vue'
import { loadRemote, remoteComponent } from '@fulgurjs/federation/runtime'
const RemoteButton = remoteComponent('remote-vue/Button')
const result = ref('尚未计算')
async function calculate() {
try {
const math = await loadRemote<{ add(a: number, b: number): number }>('remote-vue/math')
result.value = String(math.add(1, 2))
} catch (error) {
result.value = error instanceof Error ? error.message : String(error)
}
}
</script>
<template>
<RemoteButton label="远程按钮" />
<button @click="calculate">调用远程加法函数</button>
<p>{{ result }}</p>
</template>这里的名字一一对应:
remote-vue/Button
└─ remotes 中的键 remote-vue
└─ 远程 exposes 中的键 ./Button(调用时省略 ./)loadRemote 返回文件导出的内容。加载 math.ts 后,仍要调用 math.add() 才会执行加法。
5. 启动并检查结果
在两个终端分别运行:
# 终端一,remote-vue 项目内
npm run dev -- --port 5174 --strictPort
# 终端二,host-vue 项目内
npm run dev -- --port 5173 --strictPort打开 http://localhost:5173,应看到能增加计数的远程按钮;点击计算按钮应显示 3。使用 pnpm 的项目也可用 pnpm dev 启动。
完整工程与生产部署配置见 Vue examples。
React 怎么接入
配置方式与 Vue 相同,只需换成 React 插件、共享依赖和浏览器导入入口。
在两个已有 React + Vite 项目中:
vite.config.ts使用@vitejs/plugin-react,后面注册federation(fulgurjsConfig)。- 远程把
./Button指向./src/Button.tsx;宿主配置对应remotes地址。 - 两边用兼容的 React/renderer 版本,并共享
react、react-dom:
shared: {
react: { singleton: true, strictVersion: true },
'react-dom': { singleton: true, strictVersion: true },
}远程的 src/Button.tsx:
import { useState } from 'react'
export default function Button({ label }: { label: string }) {
const [count, setCount] = useState(0)
return <button onClick={() => setCount(count + 1)}>{label}:{count}</button>
}宿主的 src/App.tsx(假设 remotes 中配置的名字是 remote-react):
import { remoteComponent } from '@fulgurjs/federation/react'
// 放在模块顶层,不要在每次组件渲染时重新创建。
const RemoteButton = remoteComponent<{ label: string }>('remote-react/Button', {
fallback: <p>正在加载…</p>,
})
export default function App() {
return <RemoteButton label="远程 React 按钮" />
}React 加载普通 TS 模块时可以使用 loadRemote 或 useLoadRemote,都从 /react 导入。完整工程见 React examples。
Vue 和 React 怎么互相嵌套
嵌入的是子应用:子应用创建自己的组件树,宿主提供显示区域。 不需要把 React 组件转换成 Vue 组件。
以 Vue 宿主嵌入 React 为例:
- React 远程创建
src/bridge.tsx,默认导出桥接对象:
import { defineBridgeApp } from '@fulgurjs/federation/react'
export default defineBridgeApp((props) => (
<section>React 子应用:{String(props.message ?? '')}</section>
))- 在 React 远程的
exposes中增加:
exposes: { './bridge': './src/bridge.tsx' }- Vue 宿主配置该远程地址,然后在页面中使用:
<script setup lang="ts">
import { createVueBridgeApp } from '@fulgurjs/federation/bridge/vue'
const RemoteApp = createVueBridgeApp<{ message: string }>('remote-react/bridge')
</script>
<template>
<RemoteApp :app-props="{ message: '来自 Vue 宿主' }" />
</template>跨框架宿主安装 vue、react、react-dom,共享这三个依赖。子应用只安装和共享自己的框架。React 与 react-dom 必须兼容;同页有多个 React 大版本时请按版本隔离 Demo 配置,不能只加 singleton 就认为兼容问题解决了。
反方向用 React 宿主的 createReactBridgeApp,Vue 子应用用 /runtime 的 defineBridgeApp 返回 createApp(...) 创建的应用。
跨框架桥接的最短理解
| 问题 | 答案 |
|---|---|
| 宿主和子应用各装什么框架? | 桥接宿主装并共享 vue + react + react-dom(三键全 singleton: true);子应用只装并共享自己的框架。这是硬性合同,缺一键就会出现双实例(Invalid hook call / 状态不互通) |
| shared 与 singleton 在这里的作用? | 让宿主与子应用拿到同一个框架实例;singleton 只是收敛实例,不把不兼容的大版本变成兼容 |
| 子应用入口怎么导出? | exposes: { './bridge': … } 指向的文件默认导出 defineBridgeApp(...) 的返回值;缺 mount/unmount 会报 MFU-015 |
| 宿主怎么挂载? | createVueBridgeApp('远程名/bridge') / createReactBridgeApp(...) 返回一个组件,渲染它即挂载、移除即卸载;传 appProps 与可选 sessionKey |
| URL 同步什么时候需要? | 只有「刷新/分享/前进后退要恢复子应用内部页面」时才开(routing + basePath,两端都要配置);不开发同步时子应用内部跳转不影响宿主地址,这是正常行为 |
| 会话切换与卸载怎么处理? | 登录代次用 sessionKey(换号→新代次重跑 onSession;登出→null 即卸载清空);卸载由宿主组件生命周期驱动,子应用清理逻辑抛错会封锁该容器(只能整页刷新) |
appProps 是挂载快照,不是响应式 props
挂载时顶层字段做浅拷贝传入;之后宿主替换字段不会自动更新子应用。三种实时数据通道按需选择:
| 场景 | 用什么 | 代价 |
|---|---|---|
| 子应用需要宿主的实时值(token、用户名等) | 传稳定回调(如 getToken: () => store.token),子应用调用时取到最新值 | 无重挂;适合“读” |
| 两边共享一块状态 | 把宿主 store 实例经 appProps 或 AppContext 传过去,双方订阅同一实例 | 框架响应式不跨 root,子应用需自行订阅 |
| 必须以新 props 重新初始化 | 宿主给桥接组件换 key 显式重挂(卸载→重新走完整加载链) | 全部状态重置;不要频繁触发 |
这与普通 Vue/React 组件的 props 语义不同,是跨 root 挂载的结构限制,不是 bug。两个组件树也不会自动共用 Context、provide/inject 或路由,需要显式传递或在子应用安装。普通组件加载用 remoteComponent;整个子应用嵌套用桥接工厂。Vue 不能直接用 Vue 的 remoteComponent 渲染 React 组件。
双向配置、登录切换和卸载示例见 bridge examples。
子应用路由和浏览器地址怎么同步
桥接默认不改宿主地址。比如子应用从列表进入详情,地址不变,刷新就无法凭地址恢复这条详情。
开启 URL 同步后可以这样使用:
宿主地址 /approval/list → 子应用 /list
宿主地址 /approval/detail/42 → 子应用 /detail/42接入需要同时设置两端:
- 宿主路由要接住
/approval下的所有子路径,避免详情导航把子应用卸载。 - 宿主桥接组件传
routing,其中basePath是/approval,navigation由宿主路由适配器创建。 - 子应用声明
defineBridgeApp(..., { routing: true }),用受控的 memory 路由接入通道。
Vue 使用 createVueBridgeNavigation / connectVueBridgeRouter;React 使用 createReactBridgeNavigation / createReactBridgeRouter。React 宿主需要 data router(createBrowserRouter 或 createHashRouter),不能直接换成 BrowserRouter。内置适配支持 Vue Router 4、React Router ≥6.11。
这样刷新、分享链接、前进后退能恢复路由位置;不会自动保存表单内容或业务数据。详细步骤见 URL 同步 API,可运行工程见 bridge-router Demo。
远程业务页需要用户信息或初始化时
这部分是可选功能。普通按钮、工具函数不需要它。
| 需求 | 使用什么 | 什么时候发生 |
|---|---|---|
| 宿主提供用户、取 token 的方法、store 等 | provideAppContext | 宿主加载远程业务模块前提供 |
| 远程读取这些值 | getAppContext / requireAppContext | 由远程业务代码调用 |
| 远程注册全局组件、样式或其他一次性内容 | 配置 setup 文件的默认导出 | 首次 loadRemote('远程/模块') 返回业务模块前执行 |
| 每次登录、换账号都要重新同步权限等 | 同文件具名导出 onSession | 按 sessionKey 区分登录次数 |
| 退出登录,清除共享的账号上下文 | clearAppContext | 由宿主退出流程调用;私有页面/缓存也须由宿主清理 |
sessionKey 是一次登录的编号,不是 token,也不是权限凭证。重新登录或换账号生成新编号,单纯刷新 token 不换编号。
桥接组件可以通过 getContext 在加载前取得最新信息;受控 sessionKey 为 null 表示退出,组件会卸载并停止加载。省略 sessionKey 表示未启用受控登录切换。
setup 不会由文件名或目录自动触发,必须写在联邦配置里。preloadRemote 只预载资源,不执行 setup/onSession。异步初始化写入状态前要检查 context.signal.aborted,避免退出后迟到的请求写回旧账号数据。
完整代码见 初始化与上下文 API。
多个远程页面怎么管理
宿主有很多路由对应远程页面时,可以维护一份页面表,再交给 createHostPages(Vue)或 createReactHostPages(React)。它们负责查找对应模块、缓存加载组件和显示加载/错误状态,不会自动替你创建宿主 Router。
页面表里 route 是宿主路径,spec 是远程的 exposes 键(通常省略 ./,不要重复加远程名);remotePrefixes 指定这批路径属于哪个远程。例如 /shop/home + spec: 'pages/Home' + remotePrefixes: { '/shop': 'shop' },最终加载的是 shop/pages/Home。Vue 可结合 KeepAlive 保存组件状态;React 不提供相同的保活承诺。
完整配置见 页面 API 和 pages-cli Demo。
构建与部署
宿主和远程分别执行自己的构建命令,然后分别部署:
npm run build远程默认生成 fulgurjs-remoteEntry.js 和 fulgurjs-manifest.json,宿主通过配置的 prod 地址找到它们。远程可以部署到同域子目录,也可以部署到另一个域名。
部署时核对:
- 地址与 base 一致:远程部署在
/remote-vue/时,远程 Vite 构建的base也应为/remote-vue/;宿主prod配置为/remote-vue。 - 入口及时更新:HTML、remoteEntry、manifest 使用
Cache-Control: no-cache,让浏览器重新验证最新内容。带内容哈希的 chunk 可以长缓存。 - 刷新能回到页面:宿主和独立远程的页面路由分别配置 SPA 回退;资源请求不存在时应返回 404,不要把 JS 请求回退成 HTML。
- 跨域允许访问:不同域名时,远程服务器要正确提供 CORS 响应头;开发配置不会自动替你修改生产服务器。
- 避免旧文件突然失效:发布期间保留仍被旧页面引用的 chunk,或使用能避免版本混搭的部署流程。
生产部署样例见 Vue 部署说明 与 React 部署说明。
加载失败时怎么办
| 现象 | 先检查 | 插件提供的恢复方式 |
|---|---|---|
| 远程连不上 | 远程是否启动、remotes 地址和 CORS | 超时、重试、错误占位;可配置备用入口或 fallback 模块 |
| 提示模块不存在 | 远程名/模块名 是否对应 remotes/exposes | 修正名称后重试 |
| 提示共享版本不兼容 | 两边依赖版本、requiredVersion、strictVersion、作用域 | 对齐依赖或隔离不同版本,不能靠忽略错误解决 |
| 远程静态依赖曾下载失败,服务恢复后仍失败 | 浏览器可能保留该依赖 URL 的失败记录 | 默认占位提供用户主动刷新,保留当前地址 |
| 子应用卸载失败 | 子应用清理逻辑、事件和定时器 | 该容器不再重新挂载,需刷新;同时修复清理逻辑 |
remoteComponent 和桥接组件有默认错误占位。直接调用 loadRemote,或使用 React 的 useLoadRemote,需要自己处理错误状态。fallbackModule 是你显式选择的备用模块,不会自动修复原远程。
错误中会给出代码、原因和处理建议;完整清单见 错误码手册。
Vite 8 和使用范围
支持 Vite 8 开发和生产构建,已修复此前大型应用启动挂起的问题,并通过相关回归测试。
还有两点与使用有关:
- 开发第一次打开可能重载:Vite 在准备依赖,发现新依赖时可能重新优化并刷新页面。等准备完成后再判断页面是否正常;这不是生产页面每次都会发生的行为。
- 部分共享场景会多下载文件:浏览器可能下载未采用的本地库副本。同一 singleton 作用域仍使用一个实例;你主动隔离 React 18/19 时,则可以各自使用一个实例。文件下载数量与运行时实例数量不是同一件事。
当前不提供 SSR/RSC、Node 服务端联邦、React Native、自动 JS 沙箱、自动 CSS 隔离,或 webpack script/var 产物互操作。远程全局样式和变量仍可能影响宿主;子应用内部错误也需要子应用自己的错误处理。
跨框架支持子应用级嵌套,不提供组件类型转换。多层桥接路由自动代理、跨窗口路由同步及其他路由库的内置适配也不在当前范围。完整说明及 webpack 的区别见 能力对照。
调试、类型和命令行
在应用根目录运行:
# 没有存量项目:从模板创建完整工程(见「从零开始」一节)
npx @fulgurjs/federation create
# 已有项目:生成联邦配置起步文件
npx fulgurjs init # --template <路径> 指定输出路径(是路径,不是模板编号)
npx fulgurjs explain # 查看当前应用的联邦配置
# 使用页面表时,核对宿主页面声明:
npx fulgurjs check-pages --site http://localhost:5173
# 部署到 /remote-vue/ 后,将域名替换为你的实际站点:
npx fulgurjs doctor --base https://your-site.example --apps remote-vuedoctor 的 --base 是站点地址,--apps 是要检查的部署子目录(远程部署在 /remote-vue/ 就写 remote-vue);上例检查 https://your-site.example/remote-vue/。它不会从容器名自动猜测另一个开发端口。
init 只生成联邦配置模板,不替你创建完整应用、路由或 Nginx 配置(新建完整工程用 create)。check-pages 核对页面表与远程模块声明;远程不可达会报告无法验证,不代表通过。
开发类型默认开启:插件为远程模块生成类型声明。能访问远程源码时可获得更精确的提示;不能访问时生成 any 声明,表示可以导入但没有准确类型。需要关闭时设 dts: false。详细规则见 API 手册。
高级排查可查看 window.__FULGURJS_SCOPE__、window.__FULGURJS_INFO__,或设置 FULGURJS_DEBUG。普通接入不需要修改这些对象。
API 参考
不要猜接口,也不要照搬旧任务书中的签名。查参数时使用当前手册:
| 你要查什么 | 入口 | |---|---| | 所有插件配置、remotes/shared 参数与默认值 | 插件选项 | | loadRemote/loadShare、注册远程、预载、运行时插件 | 运行时 API | | Vue/React 组件、页面和 Hooks | 中文 API 手册 | | 跨框架挂载、appProps、sessionKey、卸载规则 | 桥接 API | | URL 同步、导航取消和部署 base | 路由 API | | 英文参数说明 | English API reference |
给使用 AI 接入的项目
给 AI 明确这几件事:项目用 Vue 还是 React、加载组件还是整个子应用、远程地址、exposes 名称、是否需要登录切换和 URL 同步。
要求它先读使用指南和对应 API 章节,再改代码;沿用现有 Vite 配置,核对实际依赖版本,正确使用浏览器导入入口。参数以当前类型声明为准,不创建文档中不存在的字段。完成后检查真实挂载、交互、失败处理;启用 URL 同步时再检查深链刷新、前进后退和导航取消。
文档
- 完整 Demo 与运行步骤:基础加载、双向嵌套、URL 同步、版本隔离和 Jeecg 场景。
- 可复制运行模板:Vue×Vue、React×React、双向跨框架桥接与完整 showcase,五个 pnpm workspace 模板,复制后
pnpm install && pnpm dev即可运行。 - 迁移指南:从已有微前端方案接入。
- CHANGELOG:版本变化与迁移说明。
- 验收报告:真实 MES 业务项目(两个 SVN 项目全新副本)接入验收:dev/生产/故障恢复/HMR(历史记录:该轮曾要求大型应用停用 manualChunks,5.8.0 起已修复,可保留业务 manualChunks——共享本体自动隔离进
fulgurjs-provider-*组,不受用户分组影响)。 - 历史验收:完整Demo展示与全面复测-20261002。历史结果不能代替当前项目验收。
开发与测试
以下是开发本插件仓库的命令,不是使用者接入项目必须运行的步骤:
pnpm --dir packages/plugin install
pnpm --dir packages/plugin build
pnpm test:unit完整 fixtures 安装与浏览器测试准备见 贡献指南。CI 检查构建、类型、单测、实际安装包,以及多个 Vite 版本的浏览器场景;通过数量以对应运行记录为准。
License
MIT © chenmingye (Jason)
