第十八章:微前端与 Module Federation

第十八章:微前端与 Module Federation

18.1 什么是 Module Federation

Module Federation(模块联邦)是 Webpack 5 引入的微前端核心特性,允许应用在运行时动态加载其他应用的模块。Vite 通过社区插件实现了类似能力。

核心概念

  • 宿主(Host):主应用,消费远程模块
  • 远程(Remote):子应用,暴露模块供消费
  • 共享(Shared):共享依赖,避免重复加载

18.2 Vite 中的 Module Federation 方案

主流插件对比

插件 特点 适用场景
@originjs/vite-plugin-federation 最成熟,兼容 Webpack MF 生产环境
vite-plugin-module-federation 轻量级,快速接入 开发测试
@module-federation/vite 官方最新方案,功能完整 新项目推荐

18.3 快速搭建微前端架构

安装依赖

bash 复制代码
# 宿主和远程应用都需要安装
npm install @module-federation/vite -D

远程应用配置(子应用)

javascript 复制代码
// remote-app/vite.config.js
import { defineConfig } from 'vite'
import federation from '@module-federation/vite'
import vue from '@vitejs/plugin-vue'

export default defineConfig({
  plugins: [
    vue(),
    federation({
      name: 'remote_app',           // 远程应用名称
      filename: 'remoteEntry.js',    // 暴露的入口文件
      exposes: {
        './Button': './src/components/Button.vue',
        './store': './src/store/index.ts'
      },
      shared: ['vue', 'vue-router', 'pinia']  // 共享依赖
    })
  ],
  build: {
    target: 'esnext',
    minify: false,
    cssCodeSplit: false
  }
})

宿主应用配置(主应用)

javascript 复制代码
// host-app/vite.config.js
import { defineConfig } from 'vite'
import federation from '@module-federation/vite'
import vue from '@vitejs/plugin-vue'

export default defineConfig({
  plugins: [
    vue(),
    federation({
      name: 'host_app',
      remotes: {
        remote_app: 'http://localhost:5001/assets/remoteEntry.js'
      },
      shared: ['vue', 'vue-router', 'pinia']
    })
  ]
})

使用远程模块

vue 复制代码
<!-- host-app/src/App.vue -->
<template>
  <div>
    <h1>宿主应用</h1>
    <!-- 使用远程组件 -->
    <RemoteButton />
  </div>
</template>

<script setup>
import { defineAsyncComponent } from 'vue'

// 动态导入远程组件
const RemoteButton = defineAsyncComponent(() => import('remote_app/Button'))
</script>

18.4 高级配置

共享依赖策略

javascript 复制代码
federation({
  shared: {
    vue: {
      singleton: true,        // 确保单例
      requiredVersion: '^3.3.0',
      eager: false
    },
    'vue-router': {
      singleton: true,
      requiredVersion: '^4.2.0'
    }
  }
})

动态远程加载

javascript 复制代码
// 运行时动态加载远程模块
async function loadRemoteApp(url) {
  // 动态添加远程
  await __federation_import(`${url}/remoteEntry.js`)
  
  // 动态导入模块
  const module = await import('dynamic_remote/Component')
  return module.default
}

版本冲突处理

javascript 复制代码
federation({
  shared: (pkgName, shareConfig) => {
    // 自定义共享策略
    if (pkgName === 'vue') {
      return {
        ...shareConfig,
        version: '3.3.4',
        shareScope: 'default'
      }
    }
    return shareConfig
  }
})

18.5 与 Webpack Module Federation 互操作

Vite 消费 Webpack 远程

javascript 复制代码
// vite.config.js
federation({
  remotes: {
    webpack_app: 'webpack_app@http://localhost:3000/remoteEntry.js'
  }
})

Webpack 消费 Vite 远程

javascript 复制代码
// webpack.config.js
module.exports = {
  plugins: [
    new ModuleFederationPlugin({
      name: 'host',
      remotes: {
        vite_app: 'vite_app@http://localhost:5001/assets/remoteEntry.js'
      }
    })
  ]
}

注意:互操作时需确保:

  • 共享依赖版本兼容
  • 构建目标一致(ESM 格式)
  • 跨域配置正确

18.6 生产环境部署

跨域配置

javascript 复制代码
// 远程应用添加 CORS 头
server: {
  cors: {
    origin: ['https://host-app.com'],
    methods: ['GET', 'HEAD']
  }
}

CDN 部署

javascript 复制代码
// 根据环境动态配置
const remotes = {
  remote_app: process.env.NODE_ENV === 'production'
    ? 'remote_app@https://cdn.example.com/remoteEntry.js'
    : 'remote_app@http://localhost:5001/assets/remoteEntry.js'
}

版本管理

javascript 复制代码
// 使用版本标签
const REMOTE_VERSION = '1.2.0'
const remoteUrl = `https://cdn.example.com/${REMOTE_VERSION}/remoteEntry.js`

18.7 性能优化

预加载远程资源

html 复制代码
<!-- 在 HTML 中预加载 -->
<link rel="prefetch" href="http://localhost:5001/assets/remoteEntry.js">

懒加载路由

javascript 复制代码
// router/index.js
const routes = [
  {
    path: '/remote',
    component: () => import('remote_app/Page')
  }
]

共享依赖优化

javascript 复制代码
federation({
  shared: {
    // 只共享必要的依赖
    lodash: { singleton: false, eager: false },
    moment: { singleton: false }  // 允许重复加载
  }
})

18.8 常见问题

Q1: 样式丢失

原因 :远程组件的样式未正确打包
解决

javascript 复制代码
build: {
  cssCodeSplit: false  // 不拆分 CSS
}

Q2: 共享状态不同步

原因 :Pinia/Vuex 未正确共享
解决

javascript 复制代码
shared: {
  pinia: {
    singleton: true,
    eager: true
  }
}

Q3: TypeScript 类型提示缺失

解决:生成类型声明文件

javascript 复制代码
// 远程应用生成 .d.ts
exposes: {
  './Button': './src/components/Button.vue',
  './types': './src/types/index.ts'  // 导出类型
}

Q4: 热更新失效

原因 :远程模块不支持 HMR
解决:开发时使用本地模式,生产时使用远程模式

18.9 最佳实践

1. 微前端拆分原则

  • 按业务域拆分(用户中心、订单中心)
  • 独立团队独立部署
  • 共享依赖版本统一管理

2. 通信机制

javascript 复制代码
// 使用 CustomEvent 进行跨应用通信
// 发送事件
window.dispatchEvent(new CustomEvent('app-event', {
  detail: { type: 'USER_LOGGED_IN', data: user }
}))

// 监听事件
window.addEventListener('app-event', (e) => {
  console.log('收到消息:', e.detail)
})

3. 错误边界

vue 复制代码
<template>
  <Suspense>
    <RemoteComponent />
    <template #fallback>
      <div>加载中...</div>
    </template>
  </Suspense>
</template>

<script setup>
import { onErrorCaptured } from 'vue'

onErrorCaptured((err) => {
  console.error('远程组件加载失败:', err)
  return false  // 阻止错误继续传播
})
</script>

18.10 未来趋势

Vite 原生 Module Federation

Vite 团队正在推进原生支持,未来可能无需插件:

  • 更快的热更新
  • 更好的开发体验
  • 与 Vite 生态深度集成

与 RSC 的结合

React Server Components + Module Federation 可能成为新的微前端方案。


本章小结

Module Federation 让 Vite 项目具备了微前端能力:

  • 独立开发、独立部署
  • 运行时动态集成
  • 共享依赖避免重复加载

选择合适插件,遵循最佳实践,可以构建可扩展的微前端架构。

下一步:下一章将介绍 Vite 项目的性能监控与分析。

相关推荐
三声三视2 小时前
React 18 并发渲染实战:useTransition、Suspense 与自动批处理深度解析
前端·javascript·react.js
不会写DN2 小时前
从零打造一个丝滑的 Vue 3 返回顶部组件
前端·javascript·vue.js
架构师老Y2 小时前
010:API网关调试手记:路由、认证与限流的那些坑
开发语言·前端·python
前端老石人2 小时前
无障碍访问
开发语言·前端·html
黑金IT2 小时前
AI带‘脑’摒弃前端硬编码实现浏览器自动化系统
前端·人工智能·自动化
榴莲omega2 小时前
第13天:CSS(二)| Grid 布局完全指南
前端·css·js八股
牧杉-惊蛰2 小时前
修改表格选中时的背景色与鼠标滑过时的背景色
前端·javascript·css·vue.js·elementui·html
彧翎Pro2 小时前
前端状态管理进化史:从Redux到Zustand的范式转变
前端·javascript
bjzhang752 小时前
使用 HTML + JavaScript 实现表格滚动效果
前端·javascript·html·表格滚动效果