如何利用 `Vite` 的 `module federation` 插件,实现 Vue 微前端架构下的代码共享和版本管理?

各位观众老爷,晚上好!我是你们的老朋友,今天咱们不聊八卦,就聊聊技术——聊聊怎么用 Vitemodule federation 插件,把 Vue 微前端架构玩出新花样,实现代码共享和版本管理。

一、 啥是 Module Federation? 别跟我扯一堆概念!

先别晕,Module Federation 其实没那么玄乎。你可以把它想象成乐高积木,每个积木块(也就是模块)都可以独立开发、部署,最后拼在一起组成一个完整的房子(也就是应用)。

  • 传统方式的痛点: 假设你有三个 Vue 项目,都要用到一个公用的按钮组件。 传统的做法就是把这个组件复制粘贴到每个项目里。 问题来了:

    • 代码冗余: 占用空间不说,改一个地方要改三个地方,简直是噩梦。
    • 版本不一致: 某个项目偷懒没更新,就导致各个项目用的按钮样式不一样,UI 风格不统一,甲方爸爸会骂娘的!
  • Module Federation 的优势: Module Federation 允许一个应用(host)动态地加载另一个应用(remote)的模块,并且这些模块可以共享依赖。 也就是说,那个公用的按钮组件可以放在一个单独的项目里,其他项目直接引用,避免了代码冗余和版本不一致的问题。

二、 Vite + Module Federation: 天生一对!

Vite 快,那是公认的。 Module Federation 可以把应用拆成小块,按需加载,进一步提升性能。 两者结合,简直是微前端架构的最佳拍档!

三、 实战演练: 手把手教你搭建一个简单的微前端 Demo

为了更好地理解,咱们来做一个简单的 Demo,包含一个 Host 应用和一个 Remote 应用,Remote 应用暴露一个组件给 Host 应用使用。

1. 创建项目

首先,用 Vite 创建两个 Vue 项目:

npm create vite@latest host-app --template vue
npm create vite@latest remote-app --template vue

2. 安装 Module Federation 插件

进入两个项目的根目录,安装 @originjs/vite-plugin-federation 插件:

cd host-app
npm install @originjs/vite-plugin-federation --save-dev

cd ../remote-app
npm install @originjs/vite-plugin-federation --save-dev

3. 配置 Remote 应用 (remote-app)

Remote 应用负责暴露组件。

  • 创建组件:remote-app/src/components 目录下创建一个 RemoteButton.vue 组件:

    <template>
      <button @click="handleClick">Remote Button (Version: {{ version }})</button>
    </template>
    
    <script setup>
    import { ref } from 'vue';
    
    const version = '1.0.0'; // 组件版本号
    const emit = defineEmits(['click']);
    
    const handleClick = () => {
      emit('click');
    };
    </script>
  • 修改 vite.config.js 配置 Module Federation:

    import { defineConfig } from 'vite'
    import vue from '@vitejs/plugin-vue'
    import federation from "@originjs/vite-plugin-federation";
    
    export default defineConfig({
      plugins: [
        vue(),
        federation({
          name: 'remote_app', // 必须唯一
          filename: 'remoteEntry.js', // 远程入口文件名
          exposes: {
            './RemoteButton': './src/components/RemoteButton.vue', // 暴露的模块
          },
          shared: ['vue'] // 共享的依赖
        })
      ],
      server: {
        port: 5001, // 端口号
      }
    })
    • name:Remote 应用的名称,必须唯一。
    • filename:远程入口文件名,Host 应用会通过这个文件加载 Remote 应用暴露的模块。
    • exposes:暴露的模块,键是模块的名称,值是模块的路径。
    • shared:共享的依赖,Remote 应用和 Host 应用都会用到 vue,所以需要共享,避免重复加载。
  • 启动 Remote 应用:

    npm run dev

4. 配置 Host 应用 (host-app)

Host 应用负责加载 Remote 应用暴露的组件。

  • 修改 vite.config.js 配置 Module Federation:

    import { defineConfig } from 'vite'
    import vue from '@vitejs/plugin-vue'
    import federation from "@originjs/vite-plugin-federation";
    
    export default defineConfig({
      plugins: [
        vue(),
        federation({
          name: 'host_app', // 必须唯一
          remotes: {
            remote_app: 'http://localhost:5001/remoteEntry.js', // 远程应用地址
          },
          shared: ['vue'] // 共享的依赖
        })
      ],
      server: {
        port: 5000, // 端口号
      }
    })
    • name:Host 应用的名称,必须唯一。
    • remotes:远程应用列表,键是 Remote 应用的名称,值是 Remote 应用的远程入口文件地址。
    • shared:共享的依赖,和 Remote 应用保持一致。
  • 使用 Remote 组件:host-app/src/App.vue 中引入 Remote 组件:

    <template>
      <h1>Host App</h1>
      <RemoteButton @click="handleClick" />
      <p>Count: {{ count }}</p>
    </template>
    
    <script setup>
    import { ref } from 'vue';
    import { defineAsyncComponent } from 'vue';
    
    // 动态加载 Remote 组件
    const RemoteButton = defineAsyncComponent(() => import('remote_app/RemoteButton'));
    
    const count = ref(0);
    
    const handleClick = () => {
      count.value++;
      alert('Button clicked from Remote App!');
    };
    </script>
    • defineAsyncComponent:异步加载 Remote 组件,避免阻塞主应用。
    • remote_app/RemoteButton:Remote 组件的名称,和 Remote 应用的 exposes 配置中的键对应。
  • 启动 Host 应用:

    npm run dev

5. 运行效果

分别启动 Host 应用和 Remote 应用,在浏览器中访问 http://localhost:5000,可以看到 Host 应用成功加载了 Remote 应用的 RemoteButton 组件。

四、 代码共享: 不止是组件,还能共享啥?

Module Federation 不仅仅能共享组件,还能共享:

  • 状态管理: 可以把 Vuex 或者 Pinia 的 store 放在一个单独的项目里,其他项目直接引用,实现状态共享。
  • 工具函数: 可以把常用的工具函数封装成一个模块,其他项目直接引用,提高开发效率。
  • UI 库: 可以把自定义的 UI 库放在一个单独的项目里,其他项目直接引用,统一 UI 风格。

五、 版本管理: 如何保证各个项目用的都是最新版本?

版本管理是 Module Federation 的一个重要环节。

  • 组件版本号: 在 Remote 组件中定义版本号,方便 Host 应用识别。 比如上面的 RemoteButton.vue 组件中的 version 变量。
  • CDN: 把 Remote 应用部署到 CDN 上,可以方便地更新版本,并且减少 Host 应用的打包体积。
  • 版本控制: 使用 Git 等版本控制工具管理 Remote 应用的代码,方便回滚和查看历史版本。
  • CI/CD: 使用 CI/CD 工具自动化部署 Remote 应用,保证每次更新都能及时发布。

六、 高级玩法: 动态 Remote 应用

上面的例子中,Remote 应用的地址是写死的。 如果 Remote 应用的地址经常变化,或者需要根据不同的环境加载不同的 Remote 应用,怎么办?

  • 环境变量: 可以把 Remote 应用的地址放在环境变量中,Host 应用在运行时读取环境变量,动态加载 Remote 应用。
  • 配置中心: 可以使用配置中心(比如 Apollo、Nacos)管理 Remote 应用的地址,Host 应用从配置中心获取 Remote 应用的地址。
  • 服务发现: 可以使用服务发现机制(比如 Consul、Eureka)自动发现 Remote 应用的地址,Host 应用通过服务发现机制获取 Remote 应用的地址。

七、 踩坑指南: 注意事项

  • 依赖冲突: 共享的依赖版本要保持一致,否则可能会出现运行时错误。 可以使用 shared 配置项的 requiredVersion 属性指定依赖的版本范围。
  • 循环依赖: 避免 Remote 应用和 Host 应用之间出现循环依赖,否则可能会导致加载失败。
  • 性能优化: 合理划分模块,避免过度拆分,否则可能会增加网络请求,影响性能。
  • 安全问题: 注意 Remote 应用的安全问题,防止恶意代码注入。

八、 总结

Module Federation 是一种强大的微前端架构解决方案,可以实现代码共享和版本管理,提高开发效率和代码质量。 Vitemodule federation 插件,让这一切变得更加简单。 当然,Module Federation 也有一些缺点,比如配置复杂、调试困难等。 在实际项目中,需要根据具体情况选择合适的微前端架构方案。

希望今天的讲解对你有所帮助,祝你早日成为微前端架构大师! 下次有机会再和大家分享更多有趣的技术!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注