@Dale-Lin
2022-07-24T12:05:00.000000Z
字数 2738
阅读 724
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").containermodule.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").containermodule.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.jsconst app1UtilsSayHello = async () => {const { sayHello } = await import("app1ImportName/utils");sayHello()}app1UtilsSayHello()
为了避免重复打包产物(例如loadash、react、vue等),Module Federation 提供了 shared 配置用于声明该应用可被共享的依赖模块:
两个应用都声明
shared的模块才能被复用。
// app1/webpack.config.jsmodule.exports = {// ...plugins: [new MoudleFederationPlugin({name: "app1",filename: `remoteEntry.js`,exposes: {"./utils": "./src/uitls","./foo": "./src/foo"},// 可被共享的依赖shared: ['lodash']})]}
// app2/webpack.config.jsmodule.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其他配置