初始模块联邦(Module Federation)

961 阅读4分钟

什么是模块联邦(Module Federation)

以前多个应用之间如果要共享代码的话,需要发布到npm上。并且代码每次改动都要重新发布npm,并且需要对依赖包进行升级,相对来说还是有点小麻烦的。

Module Federation是webpack5的新特性,Module Federation 主要是用来解决多个应用之间代码共享的问题,可以让我们的更加优雅的实现跨应用的代码共享。

模块联邦的基础使用

Module Federation是webpack5的新特性,所以项目都需要是webpack5的。

实现的效果:在A应用内有个公共的组件,然后通过模块联邦的方式共享给B应用

在A应用内进行配置:

公共组件代码:

import React from "react";
const Home = () => {
  return <div className="container">这是一个公共组件,用来测试模块联邦</div>;
};

export default Home;

webpack配置代码、用于抛出:

1.首先使用 const { ModuleFederationPlugin } = require("webpack").container 导入
2.然后配置 ModuleFederationPlugin 插件
name 必须,当前应用的名字,全局唯一ID,通过 name/{expose} 的方式使用
filename 可选,打包后的文件名,一般是remoteEntry.js
exposes:需要暴露的模块,使用时通过 name/{name}/{expose} 引用
配置项的key 必须是 ./xxx,因为要通过 ${name}/xxx 进行引用,所以./ 是必须的
配置项的value是当前的组件的路径

相关代码:

const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
// ... 删除多余代码
const { ModuleFederationPlugin } = require("webpack").container;

module.exports = () => {
  return {
    // ... 删除多余代码
    plugins: [
      new ModuleFederationPlugin({
        name: "mfyyds",
        filename: `remoteEntry.js`,
        exposes: {
          "./HomeMF": "./src/Home/Home.js",
        },
      }),
    ],
    module: {  // ... 删除多余代码 }, };
};

在B应用内进行引入和使用:

webpack配置代码、用于引入:

  • 首先使用 const { ModuleFederationPlugin } = require("webpack").container 导入

  • 然后配置 ModuleFederationPlugin 插件

  • name 必须,当前应用的名字,全局唯一ID

  • remotes:引入模块

  • 配置项的key 对应的是A应用里的 name

  • 配置项的value是模块具体地址,有两部分组成:`@`

  • name 是A应用的唯一name

  • url 是模块构建的文件地址,本地就是localhost

相关代码:

const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
// ... 删除多余代码
const { ModuleFederationPlugin } = require("webpack").container;

module.exports = () => {
  return {
    // ... 删除多余代码
    plugins: [
      new ModuleFederationPlugin({
        name: "container",
        // 远程引用的应用及其别名的映射
        remotes: {
          // key 是模块的别名,作为当前应用中引入该模块时的 name
          // value 是模块具体地址,有两部分组成:`<name>@<url>`
          //   name 是模块自己配置的名称
          //   url 是模块构建的文件地址
          mfyyds: "mfyyds@http://localhost:8080/remoteEntry.js",
        },
      }),
    ],
    // ... 删除多余代码
  };
};

在代码内进行使用:

  • 使用React.lazy(() => import("mfyyds/HomeMF")); 进行引用

  • mfyyds 是 A应用的唯一name

  • HomeMF 是在A应用内 expose 配置项的key

相关代码:

import React, { Suspense } from "react";
const HomeMF = React.lazy(() => import("mfyyds/HomeMF"));

const App = () => {
  return (
    <div className="container">
      22222
      <Suspense fallback="Loading Button">
        <HomeMF />
      </Suspense>
    </div>
  );
};

export default App;

Module Federation 配置项如下:

Host。消费模块的一方。Remote。提供模块的一方。

name: 必须且唯一。

filename: 若没有提供 filename,那么构建生成的文件名与容器名称同名。一般remoteEntry.js

remotes: 可选,作为引用方最关键的配置项,用于声明需要引用的远程资源包的名称与模块名称,作为 Host 时,去消费哪些 Remote。

// 在此项配置中,会从远程的 http://localhost:3000/app-entry.js 读取文件来加载 app 模块
new ModuleFederationPlugin({
    name: "template",
    remotes: {
        app: "http://localhost:3000/app-entry.js"
    }
})

exposes: 可选,表示作为 Remote 时,export 哪些属性被消费

// 此项配置会相当于建立一个新的entry入口从 ./src/button.js 文件开始进行打包生产新的资源
//   从这个entry打包的资源不会与webpack主入口的资源共享内容。即两个入口都引用了react的话,react会在两个项目中分别存在
new ModuleFederationPlugin({
    name: "template",
    exposes: {
        button: "./src/button.js"
    }
})

**shared:**指示 remote 应用的输出内容和 host 应用可以共用哪些依赖。shared 要想生效,则 host 应用和 remote 应用的 shared 配置的依赖要一致。

// 哪些模块会在主项目和远程项目中共享
// - 例如:当主项目提供了react的话,远程项目则会直接使用主项目的react模块,而不会再次从远程加载(远程项目的react会单独打包)
// - 主项目的shared集合需要包含远程项目的shared
// - eager: 共享依赖在打包过程中是否被分离为 async chunk。设置为 true, 共享依赖会打包到 main、remoteEntry,不会被分离,因此当设置为true时共享依赖是没有意义的
// - singleton: 是否开启单例模式。默认值为 false,开启后remote 应用组件和 host 应用共享的依赖只加载一次,而且是两者中版本比较高的。
new ModuleFederationPlugin({
    name: "template",
    shared: {
        react: {
            eager: true
        }
    }
})