第十八章:微前端与 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 项目的性能监控与分析。

相关推荐
网络点点滴17 小时前
前端与后端的区别与联系
前端
EnCi Zheng17 小时前
M5-markconv自定义CSS样式指南 [特殊字符]
前端·css·python
kyriewen17 小时前
你的网页慢,用户不说直接走——前端性能监控教你“读心术”
前端·性能优化·监控
广州华水科技17 小时前
北斗GNSS变形监测在大坝安全监测中的应用与优势分析
前端
前端老石人17 小时前
前端开发中的 URL 完全指南
开发语言·前端·javascript·css·html
CAE虚拟与现实17 小时前
五一假期闲来无事,来个前段、后端的说明吧
前端·后端·vtk·three.js·前后端
Sarvartha18 小时前
三目运算符
linux·服务器·前端
晓晨的博客18 小时前
ROS1录制的bag包转换为ROS2格式
前端·chrome
Wect18 小时前
LeetCode 72. 编辑距离:动态规划经典题解
前端·算法·typescript
donecoding18 小时前
别再让 pnpm 跟着 nvm 跑了!独立安装终极指南
前端·node.js·前端工程化