Vite插件:模块联邦核心实现
npm install @jiayouzuo/vite-module-federation-coreVite 模块联邦插件,支持微前端架构中的模块共享与远程加载。
``bash`
pnpm add @jiayouzuo/vite-module-federation-core
- 支持 exposes 暴露模块remotes
- 支持 消费远程模块shared
- 支持 共享依赖exposes
- 支持 和 remotes 同时使用singleton
- 支持 单例模式strictVersion
- 支持 严格版本校验
- 支持开发环境和生产环境
- 支持 Vue 和 React 项目
---
`ts
// 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
}
})
`
`ts
// 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
主应用
`
---
`ts
// 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
}
})
`
`ts
// 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
}
})
`
`tsx
// 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 (
export default App
`
---
`ts
// 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 |default
| exposes | object | 否 | 暴露的模块,key 为导出路径,value 为文件路径 |
| remotes | object | 否 | 远程模块,key 为引用名称,value 为入口地址 |
| shared | array/object | 否 | 共享依赖配置 |
| shareScope | string | 否 | 共享作用域,默认 |
`ts
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']
`
`ts
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'
}
}
`ts
exposes: {
// 简单写法
'./Button': './src/components/Button.vue',
// 完整配置
'./Card': {
import: './src/components/Card.vue',
// 是否阻止样式自动注入到 head
dontAppendStylesToHead: false
}
}
`
---
为远程模块添加类型声明:
`ts
// 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
export default Button
}
declare module 'remoteReactApp/hooks' {
export function useCounter(): {
count: number
increment: () => void
}
}
`
---
1. build.target 需要设置为 es2022 或更高(因为使用了顶层 await、??=` 等语法)
2. React 项目必须对 react 和 react-dom 开启 singleton,否则会报 hooks 相关错误
3. Vue 项目建议对 vue 开启 singleton,避免多实例问题
4. 共享依赖版本需要兼容,主应用和远程应用的依赖版本差异过大可能导致问题
5. 开发环境和生产环境地址不同,可通过环境变量区分配置
MIT