Vite&Module Federation(模块联邦)

摘自 深入浅出 Vite

说明

  • 模块联邦中主要有两种模块: 本地模块远程模块
  • 本地模块即为普通模块,是当前构建流程中的一部分,而远程模块不属于当前构建流程,在本地模块的运行时进行导入,同时本地模块和远程模块可以共享某些依赖的代码
  • 在模块联邦中,每个模块既可以是本地模块,导入其它的远程模块,又可以作为远程模块,被其他的模块导入

主要优势

  1. 实现任意粒度的模块共享 。这里所指的模块粒度可大可小,包括第三方 npm 依赖、业务组件、工具函数,甚至可以是整个前端应用!而整个前端应用能够共享产物,代表着各个应用单独开发、测试、部署,这也是一种微前端的实现。
  2. 优化构建产物体积。远程模块可以从本地模块运行时被拉取,而不用参与本地模块的构建,可以加速构建过程,同时也能减小构建产物。
  3. 运行时按需加载 。远程模块导入的粒度可以很小,如果你只想使用 app1 模块的add函数,只需要在 app1 的构建配置中导出这个函数,然后在本地模块中按照诸如import('app1/add')的方式导入即可,这样就很好地实现了模块按需加载。
  4. 第三方依赖共享 。通过模块联邦中的共享依赖机制,我们可以很方便地实现在模块间公用依赖代码,从而避免以往的external + CDN 引入方案的各种问题。

使用

  • vite部分主要以@originjs/vite-plugin-federation 为准
sql 复制代码
yarn add @originjs/vite-plugin-federation -D

远端模块 remote 配置

javascript 复制代码
// remote/vite.config.ts
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import federation from "@originjs/vite-plugin-federation";

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [
    vue(),
    // 模块联邦配置
    federation({
      name: "remote_app",
      filename: "remoteEntry.js",
      // 导出模块声明
      exposes: {
        "./Button": "./src/components/Button.js",
        "./App": "./src/App.vue",
        "./utils": "./src/utils.ts",
      },
      // 共享依赖声明
      shared: ["vue"],
    }),
  ],
  // 打包配置
  build: {
    target: "esnext",
  },
});

本地模块 host 配置

javascript 复制代码
// 本地模块配置
// host/vite.config.ts
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import federation from "@originjs/vite-plugin-federation";

export default defineConfig({
  plugins: [
    vue(),
    federation({
      // 远程模块声明
      remotes: {
        remote_app: "http://localhost:3001/assets/remoteEntry.js",
      },
      // 共享依赖声明
      shared: ["vue"],
    }),
  ],
  build: {
    target: "esnext",
  },
});

随后对远端模块内容进行打包,并 预览,模拟CDN部署效果。

scss 复制代码
// 打包产物
pnpm run build
// 模拟部署效果,一般会在生产环境将产物上传到 CDN 
npx vite preview --port=3001 --strictPort

便可在本地模块中进行使用

javascript 复制代码
<script setup lang="ts">
import HelloWorld from "./components/HelloWorld.vue";

import { defineAsyncComponent } from "vue";

// 导入远程模块
// 1. 组件
import RemoteApp from "remote_app/App";
// 2. 工具函数
import { add } from "remote_app/utils";
// 3. 异步组件
const AysncRemoteButton = defineAsyncComponent(
  () => import("remote_app/Button")
);
const data: number = add(1, 2);
</script>

<template>
  <div>
    <img alt="Vue logo" src="./assets/logo.png" />
    <HelloWorld />
    <RemoteApp />
    <AysncRemoteButton />
    <p>应用 2 工具函数计算结果: 1 + 2 = {{ data }}</p>
  </div>
</template>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

这样便可以在本地项目中使用远端的内容了。

总体的流程为:

  1. 远程模块通过exposes 注册导出的模块,本地模块通过 remotes 注册远程模块地址。
  2. 远程模块进行构建,并部署到云端。
  3. 本地通过import '远程模块名称/xxx'的方式来引入远程模块,实现运行时加载。

且:

  • 在模块联邦中的配置中,exposesremotes参数其实并不冲突,也就是说一个模块既可以作为本地模块,又可以作为远程模块。
  • 由于 Vite 的插件机制与 Rollup 兼容,vite-plugin-federation方案在 Rollup 中也是完全可以使用的。

实现原理

  • 对于共享依赖而言
  • 流程图
相关推荐
程序猴老王25 分钟前
el-select 和el-tree二次封装
前端·vue.js·elementui
blzlh37 分钟前
手把手教你做网易云H5页面,进大厂后干的第一件事
前端·javascript·css
贩卖纯净水.1 小时前
网站部署及CSS剩余模块
前端·css
Justinc.1 小时前
CSS3_BFC(十二)
前端·css·css3
刺客-Andy1 小时前
React第四节 组件的三大属性之state
前端·javascript·react.js
黄毛火烧雪下1 小时前
React 表单Form 中的 useWatch
前端·javascript·react.js
爱健身的小刘同学2 小时前
钉钉免登录接口
前端·javascript·钉钉
啵咿傲2 小时前
跨域相关的一些问题 ✅
前端
命运之光2 小时前
生日主题的烟花特效HTML,CSS,JS
前端·javascript·css
Cshaosun2 小时前
js版本之ES5特性简述【String、Function、JSON、其他】(二)
前端·javascript·es