什么是Module Federation2

180 阅读5分钟

Module Federation2概念

模块联合是一种 JavaScript 应用去中心化的架构模式(类似于服务器端的微服务)。它允许您在多个 JavaScript 应用程序(或微前端)之间共享代码和资源。这可以帮助您:

  1. 减少代码重复

  2. 提高代码可维护性

  3. 降低应用程序的整体大小

  4. 增强应用程序的性能

module federationv1和v2的区别是什么?

Module Federation 2.0 与 Webpack5 内置的 Module Federation 不同,它不仅提供模块导出、加载和依赖共享等核心功能,还提供额外的动态类型提示、Manifest、Federation Runtime 和 Runtime Plugin System。这些特性使得 Module Federation 更适合用作大型 Web 应用程序中的微前端架构。

特点

  • 代码共享、依赖复用
  • 资源清单(Manifest)
  • module federation runtime
  • 运行时插件系统(tapable实现)
  • 动态类型提示
  • Chrome 开发工具
  • Rspack 和 Webpack 支持

模块联邦并没有样式隔离机制, 这意味着, 当主子应用很有可能会互相造成样式污染

概念理解:生产者

通过 Module federation 构建插件设置了 exposes 暴露其他模块给其他 JavaScript 应用消费的应用在 Module federation 中称之为 Provider(生产者),生产者可以同时作为一个消费者。

概念理解:消费者

通过 Module federation 构建插件设置了 remotes 消费其他生产者的模块称之为 Consumer (消费者),消费者可以同时作为一个生产者。

@module-federation/enhanced

Module Federation 核心包,作为 Webpack 构建插件、 Rspack 构建插件、Runtime 入口依赖。

@module-federation/runtime

Module Federation 的 Runtime 包,通常使用 @module-federation/enhanced 来使用 Runtime 能力,若不需要使用构建工具时可单独安装此包。

概念目标

  • 它既可以暴露,又可以使用 webpack 支持的任何模块类型

  • 代码块加载应该并行加载所需的所有内容(web:到服务器的单次往返)

  • 从使用者到容器的控制

    • 重写模块是一种单向操作
    • 同级容器不能重写彼此的模块。
  • 概念适用于独立于环境

    • 可用于 web、Node.js 等
  • 共享中的相对和绝对请求

    • 会一直提供,即使不使用
    • 会将相对路径解析到 config.context
    • 默认不会使用 requiredVersion
  • 共享中的模块请求

    • 只在使用时提供
    • 会匹配构建中所有使用的相等模块请求
    • 将提供所有匹配模块
    • 将从图中这个位置的 package.json 提取 requiredVersion
    • 当你有嵌套的 node_modules 时,可以提供和使用多个不同的版本
  • 共享中尾部带有 /  的模块请求将匹配所有具有这个前缀的模块请求


使用

目前 Module Federation 提供了两种注册模块和加载模块的方式:

  1. 一种是在构建插件中声明(一般是在 module-federation.config.ts 文件中声明)

  2. 另一种方式是直接通过 runtime 的 api 进行模块注册和加载。

两种模式并不冲突可结合使用。你可以根据你的实际场景灵活选取模块注册方式和时机

运行时注册模块和构建配置注册模块的区别如下:

运行时注册模块插件中注册模块
可脱离构建插件使用,在 webpack4 等项目中可直接使用纯运行时进行模块注册和加载构建插件需要是 webpack5 或以上
支持动态注册模块不支持动态注册模块
不支持 import 语法加载模块支持 import 同步语法加载模块
支持 loadRemote 加载模块支持 loadRemote 加载模块
设置 shared 必须提供具体版本和实例信息设置 shared 只需要配置规则即可,无须提供具体版本及实例信息
shared 依赖只能供外部使用,无法使用外部 shared 依赖shared 依赖按照特定规则双向共享
可以通过 runtime 的 plugin 机制影响加载流程目前不支持提供 plugin 影响加载流程
不支持远程类型提示支持远程类型提示

插件用法

快速创建项目

Module Federation 提供了 create-module-federation 工具来创建项目,不需要全局安装,直接使用 npx 按需运行即可。

模板

在创建项目时,你可以选择 create-module-federation 提供的下列模板:

模板描述
provider-modern使用 Modern.js 的生产者
provider-rsbuild使用 Rsbuild 的生产者
provider-rslib使用 Rslib 的生产者
provider-rslib-storybook使用 Rslib 的生产者,并且开启了 storybook 功能
consumer-modern使用 Modern.js 的消费者
consumer-rsbuild使用 Rsbuild 的消费者

生产者, 提供组件

# 创建 my-project目录下创建mf-provider的生产者
npx create-module-federation --dir my-project --template provider-modern --name mf-provider
import { createModuleFederationConfig } from "@module-federation/rsbuild-plugin";

export default createModuleFederationConfig({
  name: "mf-provider",
  exposes: {
    "./Provider": "./src/components/ProviderComponent.tsx",
  },
  shared: {
    react: { singleton: true },
    "react-dom": { singleton: true },
  },
});

消费者, 使用生产者

# 创建 my-project目录下创建mf-consumer的消费者
npx create-module-federation --dir my-project --template consumer-modern --name mf-consumer

import { createModuleFederationConfig } from "@module-federation/rsbuild-plugin";

export default createModuleFederationConfig({
  name: "mf-consumer",
  remotes: {
    provider: "mf-provider@http://localhost:3001/mf-manifest.json",
  },
  shareStrategy: "loaded-first",
  shared: {
    react: { singleton: true },
    "react-dom": { singleton: true },
  },
});

import Provider from "provider/Provider";
<Provider></Provider>;

runtime用法

module federation v2允许我们在写运行时代码去加载远程模块,但是远程模块的导出还是交给了构建工具基于@module-federation/runtime去实现

example1

example2

这个git项目内还有很多使用案例

模块加载

// 如果没有使用构建插件,那么可以创建新的实例,并注册模块
import { createInstance } from '@module-federation/enhanced/runtime';
import React from 'react';

const mf = createInstance({
  name: 'mf_host',
  remotes: [
    {
      name: 'remote1',
      alias: 'remote-1',
      entry: 'http://localhost:3001/mf-manifest.json',
    }
  ]
});

export default () => {
  const MyButton = React.lazy(() =>
    mf.loadRemote('remote1').then(({ MyButton }) => {
      return {
        default: MyButton
      };
    }),
  );

  return (
    <React.Suspense fallback="Loading Button">
      <MyButton />
    </React.Suspense>
  );
}

原理

image.png

  • 通过share的模块回自动在执行链路过程中生成share scope,而本地host和remote都会进行提供相对应的modules, 在进行使用的时候通过校验share scope中的provided模块是否符合当前的版本等信息,如果符合则进行加载模块并存储,从而达到了共享模块的母的

  • 模块加载是通过异步加载,只有在使用的时候才会进行加载对应的模块内容,在未加载前只存储对应的相关模块属性信息

juejin.cn/post/704812…