@jiayouzuo/vite-module-federation-core
v0.6.5
Published
Vite插件:模块联邦核心实现
Downloads
421
Maintainers
Readme
@jiayouzuo/vite-module-federation-core
Vite 模块联邦插件,支持微前端架构中的模块共享与远程加载。
安装
pnpm add @jiayouzuo/vite-module-federation-core功能特性
- 支持
exposes暴露模块 - 支持
remotes消费远程模块 - 支持
shared共享依赖 - 支持
exposes和remotes同时使用 - 支持
singleton单例模式 - 支持
strictVersion严格版本校验 - 支持开发环境和生产环境
- 支持 Vue 和 React 项目
Vue 示例
远程应用(暴露模块)
// remote-vue-app/vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import federation from '@jiayouzuo/vite-module-federation-core'
export default defineConfig({
plugins: [
vue(),
federation({
// 模块名称,主应用通过此名称引用
name: 'remoteVueApp',
// 远程入口文件名
filename: 'remoteEntry.js',
// 暴露的模块,key 为导出名,value 为文件路径
exposes: {
'./Button': './src/components/Button.vue',
'./Card': './src/components/Card.vue',
'./utils': './src/utils/index.ts'
},
// 共享依赖,避免重复加载
shared: {
vue: {
singleton: true, // 单例模式,确保只有一个 Vue 实例
requiredVersion: '^3.0.0'
}
}
})
],
// 需要 es2022 或更高
build: {
target: 'es2022',
minify: false
}
})主应用(消费远程模块)
// host-vue-app/vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import federation from '@jiayouzuo/vite-module-federation-core'
export default defineConfig({
plugins: [
vue(),
federation({
name: 'hostVueApp',
// 远程模块配置
remotes: {
// key: 引用名称,value: 远程入口地址
remoteVueApp: 'http://localhost:5001/assets/remoteEntry.js'
},
// 共享依赖配置需与远程应用一致
shared: {
vue: {
singleton: true,
requiredVersion: '^3.0.0'
}
}
})
],
build: {
target: 'es2022',
minify: false
}
})使用远程 Vue 组件
<!-- host-vue-app/src/App.vue -->
<script setup lang="ts">
import { defineAsyncComponent } from 'vue'
// 方式1: 动态导入(推荐,支持懒加载)
const RemoteButton = defineAsyncComponent(() => import('remoteVueApp/Button'))
const RemoteCard = defineAsyncComponent(() => import('remoteVueApp/Card'))
// 方式2: 导入工具函数
import { formatDate } from 'remoteVueApp/utils'
</script>
<template>
<div>
<h1>主应用</h1>
<!-- 使用远程组件 -->
<RemoteButton @click="handleClick">远程按钮</RemoteButton>
<RemoteCard title="远程卡片" />
</div>
</template>React 示例
远程应用(暴露模块)
// remote-react-app/vite.config.ts
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import federation from '@jiayouzuo/vite-module-federation-core'
export default defineConfig({
plugins: [
react(),
federation({
name: 'remoteReactApp',
filename: 'remoteEntry.js',
// 暴露 React 组件
exposes: {
'./Button': './src/components/Button.tsx',
'./Modal': './src/components/Modal.tsx',
'./hooks': './src/hooks/index.ts'
},
shared: {
// React 必须使用单例模式,否则会报 hooks 错误
react: {
singleton: true,
requiredVersion: '^18.0.0'
},
'react-dom': {
singleton: true,
requiredVersion: '^18.0.0'
}
}
})
],
build: {
target: 'es2022',
minify: false
}
})主应用(消费远程模块)
// host-react-app/vite.config.ts
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import federation from '@jiayouzuo/vite-module-federation-core'
export default defineConfig({
plugins: [
react(),
federation({
name: 'hostReactApp',
remotes: {
remoteReactApp: 'http://localhost:5002/assets/remoteEntry.js'
},
shared: {
react: {
singleton: true,
requiredVersion: '^18.0.0'
},
'react-dom': {
singleton: true,
requiredVersion: '^18.0.0'
}
}
})
],
build: {
target: 'es2022',
minify: false
}
})使用远程 React 组件
// host-react-app/src/App.tsx
import React, { lazy, Suspense } from 'react'
// 动态导入远程组件
const RemoteButton = lazy(() => import('remoteReactApp/Button'))
const RemoteModal = lazy(() => import('remoteReactApp/Modal'))
// 导入远程 hooks
import { useCounter } from 'remoteReactApp/hooks'
function App() {
const { count, increment } = useCounter()
return (
<div>
<h1>主应用</h1>
{/* 使用 Suspense 包裹远程组件 */}
<Suspense fallback={<div>加载中...</div>}>
<RemoteButton onClick={increment}>
点击次数: {count}
</RemoteButton>
<RemoteModal title="远程弹窗" />
</Suspense>
</div>
)
}
export default App混合使用(同时作为主应用和远程应用)
// middle-app/vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import federation from '@jiayouzuo/vite-module-federation-core'
export default defineConfig({
plugins: [
vue(),
federation({
name: 'middleApp',
filename: 'remoteEntry.js',
// 暴露自己的模块给其他应用
exposes: {
'./Header': './src/components/Header.vue',
'./Footer': './src/components/Footer.vue'
},
// 同时消费其他远程模块
remotes: {
remoteVueApp: 'http://localhost:5001/assets/remoteEntry.js',
remoteReactApp: 'http://localhost:5002/assets/remoteEntry.js'
},
shared: {
vue: { singleton: true }
}
})
],
build: {
target: 'es2022'
}
})配置项
基础配置
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| name | string | 是 | 模块名称,其他应用通过此名称引用 |
| filename | string | 否 | 远程入口文件名,默认 remoteEntry.js |
| exposes | object | 否 | 暴露的模块,key 为导出路径,value 为文件路径 |
| remotes | object | 否 | 远程模块,key 为引用名称,value 为入口地址 |
| shared | array/object | 否 | 共享依赖配置 |
| shareScope | string | 否 | 共享作用域,默认 default |
shared 配置
shared: {
// 简单写法:直接写包名
lodash: {},
// 完整配置
vue: {
// 单例模式:强制使用同一版本
// Vue/React 必须开启,否则会出现多实例问题
singleton: true,
// 严格版本:版本不匹配时抛出错误而不是回退到本地
strictVersion: false,
// 要求的版本范围
requiredVersion: '^3.0.0',
// 当前提供的版本(通常自动从 package.json 读取)
version: '3.4.0',
// 是否生成共享 chunk,默认 true
generate: true,
// 是否预加载到 HTML head
modulePreload: false
}
}
// 数组简写
shared: ['vue', 'pinia', 'lodash']remotes 配置
remotes: {
// 简单写法:直接写入口地址
remoteApp: 'http://localhost:5001/assets/remoteEntry.js',
// 完整配置
remoteApp2: {
// 远程入口地址
external: 'http://localhost:5002/assets/remoteEntry.js',
// 模块格式:esm | systemjs | var
format: 'esm',
// 来源框架:vite | webpack
from: 'vite'
},
// 动态地址(运行时决定)
dynamicApp: {
external: `Promise.resolve(window.REMOTE_URL || 'http://localhost:5003/assets/remoteEntry.js')`,
externalType: 'promise',
format: 'esm',
from: 'vite'
}
}exposes 配置
exposes: {
// 简单写法
'./Button': './src/components/Button.vue',
// 完整配置
'./Card': {
import: './src/components/Card.vue',
// 是否阻止样式自动注入到 head
dontAppendStylesToHead: false
}
}类型声明
为远程模块添加类型声明:
// src/types/remote.d.ts
// Vue 远程模块
declare module 'remoteVueApp/Button' {
import { DefineComponent } from 'vue'
const component: DefineComponent<{}, {}, any>
export default component
}
declare module 'remoteVueApp/utils' {
export function formatDate(date: Date): string
}
// React 远程模块
declare module 'remoteReactApp/Button' {
import { FC } from 'react'
interface ButtonProps {
onClick?: () => void
children?: React.ReactNode
}
const Button: FC<ButtonProps>
export default Button
}
declare module 'remoteReactApp/hooks' {
export function useCounter(): {
count: number
increment: () => void
}
}注意事项
- build.target 需要设置为
es2022或更高(因为使用了顶层 await、??=等语法) - React 项目必须对 react 和 react-dom 开启 singleton,否则会报 hooks 相关错误
- Vue 项目建议对 vue 开启 singleton,避免多实例问题
- 共享依赖版本需要兼容,主应用和远程应用的依赖版本差异过大可能导致问题
- 开发环境和生产环境地址不同,可通过环境变量区分配置
License
MIT
