Skip to content

@originjs/vite-plugin-federation

简介

模块联邦 是一种前端的类微服务架构,它允许不同应用程序在运行时共享模块。

在传统的模块化开发中,应用程序通常被拆分为多个模块,每个模块有自己的代码和功能。然而,这些模块通常是在构建时静态地组合在一起的。模块联邦通过在运行时动态加载和共享模块,提供了更灵活的模块化解决方案。

联邦模块的关键概念包括:

  • 独立开发和部署: 不同的团队可以独立地开发和部署其自己的模块。每个模块可以拥有自己的代码库、开发流程和版本管理。

  • 动态加载: 在运行时,应用程序可以动态地加载和卸载模块。这使得应用程序能够根据需要动态地获取和使用特定的模块,而不需要在构建时静态地将它们组合在一起。

  • 共享模块: 不同的应用程序可以共享彼此的模块。这意味着一个应用程序的模块可以被另一个应用程序动态地使用,实现了更高程度的代码复用。

  • Remote: 远程服务端上的模块,本地模块运行时进行导入使用,不参与当前构建流程。
  • Host: 项目中的普通模块,参与当前项目的构建流程。
  • Shared: 共享模块是指既可重写的又可作为向嵌套容器提供重写的模块。它们通常指向每个构建中的相同模块,例如相同的库。

所以使用 Module Federation 通常需要2个以上的工程,一个作为Remote端,一个作为Host端。

  • Remote 端一般用于暴露组件,这些组件共同组成一个组件库。
  • Host 端一般用于引入并使用远程模块提供的组件。

对于 Remote端和 Host 端共同依赖的模块(如 vue, jsview):

  • 如果希望使用集成在 Host端的模块,可以通过Shared方式,这样 Remote 端就不会进行该模块的的打包。
  • 如果希望使用集成在 Remote 端的模块,可以在 Remote 端暴露出来,然后 Host 端引入 Remote 暴露的模块。

提示

注意: 一般来说,共同依赖的模块只能存在一份,如果同时引入了 Remote 端和 Host 端的共同模块,会出现一些未知错误。

集成到 JsView

注意

由于JsView需要对 @originjs/vite-plugin-federation 做一些源码级调整,所以需要安装指定版本 v1.3.4,其他版本暂不支持

参考工程源码: jsview-module-federation-demo

JsView 可以选择 shared 或 expose 其中一种方式,需要 Remote 端和 Host 端配合使用,即要么两者都是 shared 方式,要么两者都是 expose 方式。

可以选择 JsView 提供的命令行自动配置或自行手动配置

JsView使用federation搭建的完整样例, 最完整工程为 host-router,其中的README进行了环境关键内容的说明: jsview-vue-federation-demo

1. 自动配置

  • Remote 端

(1) 参考 创建 HelloWorld 工程 新建一个可运行的基础工程,例如 jsview-federation-remote。

如何你已经有一个基础工程,忽略此步骤。

(2) 配置

sh
npm run tool -- --module-federation --mode=remote --jsview=shared

(3) 编译并运行 Remote Server

由于 Remote 端不支持开发模式,所以必须先进行编译。

提示

第一次编译前需要先进行App配置和生成签名,可以使用命令生成一个临时版本:

sh
npm run tool -- --config-app && npm run tool -- --gen-keypair
sh
npm run build

npx serve -C -p 8088 ./dist # 启动一个Http服务器

  • Host 端

(1) 参考 创建 HelloWorld 工程 新建一个可运行的基础工程,例如 jsview-federation-host。

如何你已经有一个基础工程,忽略此步骤。

(2) 配置和引入Remote组件

  • 执行配置脚本
sh
npm run tool -- --module-federation --mode=host --jsview=shared -remote-domain=http://localhost:8088

http://localhost:8088 需要替换成 Remote 端的 Http Server 地址

  • 引入自定义 Remote 组件, 例如:

    src/main.tsx

ts
import App from './App.vue'
改为
import App from 'RemoteEntry/App.vue'

(3) 运行 Host 开发服务

sh
npm start


2. 手动配置

  • Remote 端

(1) 参考 创建 HelloWorld 工程 新建一个可运行的基础工程,例如 jsview-federation-remote。

如何你已经有一个基础工程,忽略此步骤。

(2) 安装 @originjs/vite-plugin-federation

sh
npm install @originjs/vite-plugin-federation@1.3.4

npm ci

执行后会有一个打补丁成功的log如下图。

(3) 修改 vite.config.ts

ts
import { setFederationRemoteConfig } from './federation.remote.config'
import federation from '@originjs/vite-plugin-federation'

const viteConfig = ({
  ... // 既有配置
})

setFederationRemoteConfig(viteConfig, federation)

export default defineConfig(viteConfig)

(4) 新建 federation.remote.config.ts

ts
import federation from '@originjs/vite-plugin-federation'

function getRemoteConfig() {
  const config = {
    name: 'remote-lib',
    filename: 'remoteEntry.js',
    // 需要暴露的模块
    exposes: {
      // 自定义模块,根据需要自行配置
      './App.vue': './src/App.vue',
    },
    shared: {
      '@shijiu/jsview-vue': {},
      jsview:  { packagePath: '@shijiu/jsview-vue' },
      vue: {},
      'vue-router': {},
    },
  }

  return config
}


function setFederationRemoteConfig(config, federation) {
  config.build = (config.build || {})
  config.build.rollupOptions = (config.build.rollupOptions || {})
  config.build.rollupOptions.output = (config.build.rollupOptions.output || {})
  config.build.rollupOptions.output.manualChunks = (config.build.rollupOptions.output.manualChunks || {})
  config.build.rollupOptions.output.manualChunks["__federation_fn_import"] = ["__federation_fn_import"]

  const federationConfig = getRemoteConfig()
  config.plugins.push(federation(federationConfig))

}

export {
  setFederationRemoteConfig,
}

(5) 编译并运行 Remote Server

由于 Remote 端不支持开发模式,所以必须先进行编译。

提示

第一次编译前需要先进行App配置和生成签名,可以使用命令生成一个临时版本:

sh
npm run tool -- --config-app && npm run tool -- --gen-keypair
sh
npm run build

npx serve -C -p 8088 ./dist # 启动一个Http服务器

  • Host 端

(1) 参考 创建 HelloWorld 工程 新建一个可运行的基础工程,例如 jsview-federation-host。

如何你已经有一个基础工程,忽略此步骤。

(2) 安装 @originjs/vite-plugin-federation

sh
npm install @originjs/vite-plugin-federation@1.3.4

npm ci

执行后会有一个打补丁成功的log如下图。

(3) 修改 vite.config.ts

ts
import { setFederationHostConfig } from './federation.host.config'
import federation from '@originjs/vite-plugin-federation'

const viteConfig = ({
  ... // 既有配置
})

setFederationHostConfig(viteConfig, federation)

export default defineConfig(viteConfig)

(4) 新建 federation.host.config.ts

ts
const remoteDomain = 'http://localhost:8088' // 替换成你的 Remote 端 base url 或 ip:port;
const remoteEntryUrl = remoteDomain + '/js/remoteEntry.js'

function getHostConfig() {
  const config = {
    name: 'host-app',
    remotes: {
      'RemoteEntry': remoteEntryUrl,
    },
    shared: {
      '@shijiu/jsview-vue': {},
      jsview:  { packagePath: '@shijiu/jsview-vue' },
      vue: {},
      'vue-router': {},
    },
  }

  return config
}

function setFederationHostConfig(config, federation) {
  config.build = (config.build || {})
  config.build.rollupOptions = (config.build.rollupOptions || {})
  config.build.rollupOptions.output = (config.build.rollupOptions.output || {})
  config.build.rollupOptions.output.manualChunks = (config.build.rollupOptions.output.manualChunks || {})
  config.build.rollupOptions.output.manualChunks["__federation_fn_import"] = ["__federation_fn_import"]

  const federationConfig = getHostConfig()
  config.plugins.push(federation(federationConfig))

}

export {
  setFederationHostConfig,
}

::::

(5) src/appConfig/jsview.config.mjs 使用 Remote 端 JsViewLoader。

忽略此步, shared方式不需要从 Remote 端加载 JsView loader。

::::

(6) src/main.tsx 引入 Remote 端组件 (所有从 jsview 导入的组件均改成'RemoteEntry/JsViewVue')

ts
忽略此步, shared方式不需要从 Remote 端加载 JsView 组件。

::::

(7) 引入自定义 Remote 组件, 例如:

  • src/main.tsx
ts
import App from './App.vue'
改为
import App from 'RemoteEntry/App.vue'

(8) 删除本地组件(可选,防止相同组件出现混淆)

sh
rm -r src/components/

(9) 运行 Host 开发服务

sh
npm start

查看运行结果

采用 expose 方式或 shared 方式的 JsView 区别在于 JsView Loader 和 JsView 组件是从 Remote 加载还是从 Host 加载,通过 DevTools 可以看出结果。