Module Federation
Module Federation 是这篇文章的主角,它是 Webpack 5 新引入的一种远程模块动态加载、运行技术。MF 允许我们将原本单个巨大应用按我们理想的方式拆分成多个小体积、职责内聚的小应用形式,各个应用能够实现独立部署、独立开发、团队自洽、从而降低系统与团队协作的复杂度——这正是所谓的微前端架构。
MF 的特性有:
- 应用可按功能性导出若干模块,这些模块会被打包成模块包,功能上像 NPM 模块;
- 应用在运行时基于 HTTP(S) 协议动态加载其他应用暴露的模块,且用法与动态加载普通 NPM 包一样简单;
- 与其它微前端方案不同,MF 的应用之间关系平等,没有主应用/子应用之分,每个应用都能导出/导入任意模块。
导入与导出
MF 的最核心配置就是导出与导入,实际上两端都依赖于 ModuleFederationPlugin
插件:
- 导出方
- 使用
ModuleFederationPlugin
的expose
参数声明需要导出的模块列表 - 使用
ModuleFederationPlugin
的filename
参数声明需要导出的模块的入口文件名称 - 使用
devServer
启动开发服务器能力
- 使用
- 导入方,需要使用
ModuleFederationPlugin
的remotes
参数声明导入模块的链接- 使用
ModuleFederationPlugin
的RemoteApp
声明远程模块的地址+模块名称
- 使用
导出方配置代码解读复制代码const path = require("path"); const { ModuleFederationPlugin } = require("webpack").container; module.exports = { mode: "development", devtool: false, entry: path.resolve(__dirname, "./src/main.js"), output: { path: path.resolve(__dirname, "./dist"), // 必须指定产物的完整路径,否则使用方无法正确加载产物资源 publicPath: `http://localhost:8081/dist/`, }, plugins: [ new ModuleFederationPlugin({ // MF 应用名称 name: "app1", // MF 模块入口,可以理解为该应用的资源清单 filename: `remoteEntry.js`, // 定义应用导出哪些模块 exposes: { "./utils": "./src/utils", "./foo": "./src/foo", }, }), ], // MF 应用资源提供方必须以 http(s) 形式提供服务 // 所以这里需要使用 devServer 提供 http(s) server 能力 devServer: { port: 8081, hot: true, }, };
导入方配置代码解读复制代码const path = require("path"); const HtmlWebpackPlugin = require("html-webpack-plugin"); const { ModuleFederationPlugin } = require("webpack").container; module.exports = { mode: "development", devtool: false, entry: path.resolve(__dirname, "./src/main.js"), output: { path: path.resolve(__dirname, "./dist"), }, plugins: [ // 模块使用方也依然使用 ModuleFederationPlugin 插件搭建 MF 环境 new ModuleFederationPlugin({ // 使用 remotes 属性声明远程模块列表 remotes: { // 地址需要指向导出方生成的应用入口文件 RemoteApp: "app1@http://localhost:8081/dist/remoteEntry.js", }, }), new HtmlWebpackPlugin(), ], devServer: { port: 8082, hot: true, open: true, }, };
导入方使用位置代码解读复制代码// app-2/src/main.js (async () => { const { sayHello } = await import("RemoteApp/utils"); sayHello(); })();
依赖共享
当微服务应用中各个应用共同存在了一部分公共依赖——例如 Vue、React、Lodash 等,我们可以使用ModuleFederationPlugin
的shared
配置声明该应用可被共享的依赖模块,从而规避依赖被重复导报,造成产物冗余的问题。
注意,需要在模块导入导出双方都使用shared
配置,同时通过requireVersion
设置好相同的版本,:
css 代码解读复制代码module.exports = {
// ...
plugins: [
new ModuleFederationPlugin({
name: "app1",
filename: `remoteEntry.js`,
exposes: {
"./utils": "./src/utils",
"./foo": "./src/foo",
},
// 共享依赖及版本要求声明
+ shared: {
+ lodash: {
+ requiredVersion: "^4.17.0",
+ },
+ },
}),
}),
],
// ...
};
java 代码解读复制代码module.exports = {
// ...
plugins: [
// 模块使用方也依然使用 ModuleFederationPlugin 插件搭建 MF 环境
new ModuleFederationPlugin({
// 使用 remotes 属性声明远程模块列表
remotes: {
// 地址需要指向导出方生成的应用入口文件
RemoteApp: "app1@http://localhost:8081/dist/remoteEntry.js",
},
// 共享依赖及版本要求声明
+ shared: {
+ lodash: {
+ requiredVersion: "^4.17.0",
+ },
+ },
}),
new HtmlWebpackPlugin(),
],
// ...
};
评论记录:
回复评论: