什么是模块联邦(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:需要暴露的模块,使用时通过 {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
}
}
})