npm package: @jiayouzuo/vite-module-federation-core
Description: Vite插件：模块联邦核心实现
Latest version: 0.6.5

# @jiayouzuo/vite-module-federation-core

Vite 模块联邦插件，支持微前端架构中的模块共享与远程加载。

## 安装

```bash
pnpm add @jiayouzuo/vite-module-federation-core
```

## 功能特性

- 支持 `exposes` 暴露模块
- 支持 `remotes` 消费远程模块
- 支持 `shared` 共享依赖
- 支持 `exposes` 和 `remotes` 同时使用
- 支持 `singleton` 单例模式
- 支持 `strictVersion` 严格版本校验
- 支持开发环境和生产环境
- 支持 Vue 和 React 项目

---

## Vue 示例

### 远程应用（暴露模块）

```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 组件

```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 示例

### 远程应用（暴露模块）

```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
  }
})
```

### 使用远程 React 组件

```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 (
    <div>
      <h1>主应用</h1>
      {/* 使用 Suspense 包裹远程组件 */}
      <Suspense fallback={<div>加载中...</div>}>
        <RemoteButton onClick={increment}>
          点击次数: {count}
        </RemoteButton>
        <RemoteModal title="远程弹窗" />
      </Suspense>
    </div>
  )
}

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` |
| exposes | object | 否 | 暴露的模块，key 为导出路径，value 为文件路径 |
| remotes | object | 否 | 远程模块，key 为引用名称，value 为入口地址 |
| shared | array/object | 否 | 共享依赖配置 |
| shareScope | string | 否 | 共享作用域，默认 `default` |

### shared 配置

```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']
```

### remotes 配置

```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'
  }
}
```

### exposes 配置

```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<ButtonProps>
  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. **开发环境和生产环境地址不同**，可通过环境变量区分配置

## License

MIT