是什么
- 是Webpack 5 的新特性之一,允许在多个 webpack 编译产物之间共享模块、依赖、页面甚至应用
- 提供了一种轻量级的、在运行时,通过全局变量组合,在不同模块之前进行数据的获取
- 提供了一种解决应用集的官方方案
比如开发两个应用A和B,A应用需要引用B这个应用,假设这两个应用都处于开发阶段,那么就可以通过 webpack 的模块联邦,将B应用暴露出去,然后A应用引用B应用。这样就不需要每次B应用build完了给A,可以同步开发。
使用模块联邦,每个应用块都应该是一个独立的构建,这些构建都将编译成容器,容器可以被其他应用或容器使用
- webpack构建。一个独立项目通过webpack打包编译而产生资源包。
- remote。一个暴露模块供其他
webpakc构建消费的webpack构建。 - host。一个消费其他
remote模块的webpack构建。
注意,remote 的打包产物并不会覆盖 output 的打包产物,他俩是独立的
config 配置
const { ModuleFederationPlugin } = require('webpack').container;
module.exports = {
plugins: [
new ModuleFederationPlugin({
...
}),
],
};
主要配置
| 字段 | 含义 |
|---|---|
| name | 即输出的模块名,被远程引用时路径为 name/{expose} |
| library | 声明全局变量的方式,name为umd的name |
| filename | 构建输出的文件名 |
| remotes | 远程引用的应用名及其别名的映射,使用时以key值作为name |
| exposes | 被远程引用时可暴露的资源路径及其别名 |
| shared | 与其他应用之间可以共享的第三方依赖,使你的代码中不用重复加载同一份依赖 |
| library | 定义了 remote 应用如何将输出内容暴露给 host 应用,var,this,window等 |
- exposes
exposes是键值对,key 为暴露的组件名称,但是要写成路径,相对于remote打包后的文件,也就是说,host 引用该组件时,需要name/{expose的key};value 是该组件在当前项目中的路径 - remotes
remotes是键值对,key 作为远程应用的别名,定义了后在代码中可以动态引入使用;value 表示远程应用(remote容器)的地址,格式是远程模块的name@url地址 - shared
当前项目作为host的时候,引用远程容器,远程容器的
shared配置中的内容会复用当前项目的内容
demo
深刻理解一下exposes 和 remotes,我们来看一下伪代码,其中 Remote应用 是remote,Host应用 是host:
// Remote应用 webpack.config.js
const { ModuleFederationPlugin } = require('webpack').container;
module.exports = {
plugins: [
new ModuleFederationPlugin({
filename: "remoteEntry.js", // 构建输出的文件名
name: "remote", // 标识的模块名
exposes: {
"./Tool": "./scr/components/Tool"
}
}),
],
};
// Host应用 webpack.config.js
const { ModuleFederationPlugin } = require('webpack').container;
module.exports = {
plugins: [
new ModuleFederationPlugin({
filename: "hostEntry.js", // 即使叫remoteEntry也没关系,不要求唯一性
name: "host", // 标识的模块名
remotes: {
remoteA: "remote@http://localhost:3000/remoteEntry.js" // 我们看一下这部分,remote是指模块名,http://localhost:3000是指地址,remoteEntry.js是指暴露出的文件名
}
}),
],
};
然后我们看一下怎么用的,即在 Host应用 里使用 Remote应用
// Host应用.js
// 动态加载远程组件
const RemoteTool = React.lazy(() => import("remoteA/Tool")); // 解释:remoteA是remotes里配置的key,Tool是exposes里配置的key
...
特殊场景
- 那么,如果俩应用共享很多模块怎么办?
shared出场
我们改造 Host应用 的config
// Host webpack.config.js
const { ModuleFederationPlugin } = require('webpack').container;
module.exports = {
plugins: [
new ModuleFederationPlugin({
filename: "hostEntry.js",
name: "host",
remotes: {
remoteA: "remote@http://localhost:3000/remoteEntry.js"
},
+ shared: ['react', 'react-dom'], // 依赖的包 webpack在加载的时候会先判断本地应用是否存在对应的包,如果不存在,则加载远程应用的依赖包
],
};
shared 配置项指示 remote 应用的输出内容和 host 应用可以共用哪些依赖。 shared 要想生效,则 host 应用和 remote 应用的 shared 配置的依赖要一致。remotes 将会首先依赖来自 host 的依赖,如果 host 没有依赖,它将会下载自己的依赖
- 如果彼此都引用了呢?那就双向引用啦 也就是说,Host应用 可以引用 Remote应用,Remote应用 也可以引用 Host应用
// Remote应用 webpack.config.js
const { ModuleFederationPlugin } = require('webpack').container;
module.exports = {
plugins: [
new ModuleFederationPlugin({
filename: "remoteEntry.js",
name: "remote",
exposes: {
"./Tool": "./scr/components/Tool"
},
+ remotes: {
+ remoteB: "host@http://localhost:3000/hostEntry.js"
+ },
shared: ['react', 'react-dom']
}),
],
};
// Host应用 webpack.config.js
const { ModuleFederationPlugin } = require('webpack').container;
module.exports = {
plugins: [
new ModuleFederationPlugin({
filename: "hostEntry.js",
name: "host",
+ exposes: {
+ "./Lib": "./scr/components/Lib"
+ },
remotes: {
remoteA: "remote@http://localhost:3000/remoteEntry.js"
},
shared: ['react', 'react-dom'],
}),
],
};