@Dale-Lin
2022-07-24T20:05:00.000000Z
字数 2738
阅读 279
webpack5
Module Federation 是 webpack 5 引入的远程模块动态加载、运行技术。可以将巨石应用拆分成体积更小,职责更内聚的小应用形式,即微前端架构。
对比现有的微应用框架(例如 qiankun),区别在于:
1. Module Federation 应用之间关系平等,没有主从之分,每个应用都能导出/导入任意模块;
2. 应用可以按需导出若干模块,这些 expose 会被单独打包成模块包;
Module Federation 依赖 webpack 5 内置的 ModuleFederationPlugin
插件实现:
ModuleFederationPlugin
的 expose
参数声明需要导出的模块列表;ModuleFederationPlugin
的 remotes
声明需要从哪些地方导入远程模块。
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({
// 应用名称
name: "app1",
// 模块入口文件,包含运行时和导出的 moduleMap 等等代码
filename: 'remoteEntry.js',
// 导出的具体模块
exposes: {
"./utils": "./src/utils",
"./foo": "./src/foo"
}
})
],
devServer: {
port: 8081,
hot: true,
}
}
webpack 会把上述配置里 expose
的模块分别编译成独立产物,并将 manifest、ModuleFederation 运行时等代码打包进 filename
入口文件中,构建结果如下:
MF-basic
├─ app-1
│ ├─ dist
│ │ ├─ main.js // 正常bundle
│ │ ├─ remoteEntry.js // MF 入口文件
│ │ ├─ src_foo_js.js // MF 导出的module
│ │ └─ src_utils_js.js // MF 导出的module
│ ├─ src
│ │ ├─ ...
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: [
new ModuleFederationPlugin({
remotes: {
// `name@remoteEntryUri`
app1ImportName: "app1@http://localhost:8081/dist/remoteEntry.js"
}
}),
new HTMLWebpackPlugin()
],
devServer: {
port: 8082,
hot: true,
open: true
}
}
使用的地方用 import(yourImportName/module)
异步导入:
// app2/src/main.js
const app1UtilsSayHello = async () => {
const { sayHello } = await import("app1ImportName/utils");
sayHello()
}
app1UtilsSayHello()
为了避免重复打包产物(例如loadash、react、vue等),Module Federation 提供了 shared
配置用于声明该应用可被共享的依赖模块:
两个应用都声明
shared
的模块才能被复用。
// app1/webpack.config.js
module.exports = {
// ...
plugins: [
new MoudleFederationPlugin({
name: "app1",
filename: `remoteEntry.js`,
exposes: {
"./utils": "./src/uitls",
"./foo": "./src/foo"
},
// 可被共享的依赖
shared: ['lodash']
})
]
}
// app2/webpack.config.js
module.exports = {
// ...
plugins: [
new MoudleFederationPlugin({
remotes: {
app1ImportName: "app1@http://localhost:8081/dist/remoteEntry.js"
},
// 可被共享的依赖
shared: ['lodash']
})
]
}
上面的写法要求两个应用 shared 的模块版本一致才能复用;可以进一步用 shared.[lib].requiredVersion
写法来配置兼容的版本:
module.exports = {
// ...
plugins: [
new MoudleFederationPlugin({
// ...
shared: {
lodash: {
requriedVersion: "^4.17.0"
}
}
})
]
}
还可以通过 shared.[lib].shareScope
属性更精细地控制共享依赖模块范围:
module.exports = {
// ...
plugins: [
new MoudleFederationPlugin({
// ...
shared: {
lodash: {
sharedScope: "foo"
}
}
})
]
}
在这种配置下,其它应用所共享的 lodash 库必须同样声明为 foo
空间才能复用。shareScope
在多团队协作时能够切分出多个资源共享空间,降低依赖冲突的概率。
shared.singletong
还可以强制约束多个版本之间公用一个依赖包,如果不满足 shared.requiredVersion
则会抛出警告。
shared
其他配置