[关闭]
@Dale-Lin 2022-07-24T20:05:00.000000Z 字数 2738 阅读 279

module federation

webpack5


Module Federation 是 webpack 5 引入的远程模块动态加载、运行技术。可以将巨石应用拆分成体积更小,职责更内聚的小应用形式,即微前端架构。

对比现有的微应用框架(例如 qiankun),区别在于:
1. Module Federation 应用之间关系平等,没有主从之分,每个应用都能导出/导入任意模块;
2. 应用可以按需导出若干模块,这些 expose 会被单独打包成模块包;

使用

Module Federation 依赖 webpack 5 内置的 ModuleFederationPlugin 插件实现:


生成方配置举例:

  1. const path = require('path')
  2. const { ModuleFederationPlugin } = require("webpack").container
  3. module.exports = {
  4. mode: "development",
  5. devtool: false,
  6. entry: path.resolve(__dirname, './src/main.js'),
  7. output: {
  8. path: path.resolve(__dirname, "./dist"),
  9. publicPath:
  10. 'http://localhost:8081/dist/',
  11. },
  12. plugins: [
  13. new ModuleFederationPlugin({
  14. // 应用名称
  15. name: "app1",
  16. // 模块入口文件,包含运行时和导出的 moduleMap 等等代码
  17. filename: 'remoteEntry.js',
  18. // 导出的具体模块
  19. exposes: {
  20. "./utils": "./src/utils",
  21. "./foo": "./src/foo"
  22. }
  23. })
  24. ],
  25. devServer: {
  26. port: 8081,
  27. hot: true,
  28. }
  29. }

webpack 会把上述配置里 expose 的模块分别编译成独立产物,并将 manifest、ModuleFederation 运行时等代码打包进 filename 入口文件中,构建结果如下:

  1. MF-basic
  2. ├─ app-1
  3. ├─ dist
  4. ├─ main.js // 正常bundle
  5. ├─ remoteEntry.js // MF 入口文件
  6. ├─ src_foo_js.js // MF 导出的module
  7. └─ src_utils_js.js // MF 导出的module
  8. ├─ src
  9. ├─ ...

导入方配置举例:

  1. const path = require('path')
  2. const HTMLWebpackPlugin = require("html-webpack-plugin")
  3. const { ModuleFederationPlugin } = require("webpack").container
  4. module.exports = {
  5. mode: "development",
  6. devtool: false,
  7. entry: path.resolve(__dirname, "./src/main.js"),
  8. output: {
  9. path: path.resolve(__dirname, "./dist"),
  10. },
  11. plugins: [
  12. new ModuleFederationPlugin({
  13. remotes: {
  14. // `name@remoteEntryUri`
  15. app1ImportName: "app1@http://localhost:8081/dist/remoteEntry.js"
  16. }
  17. }),
  18. new HTMLWebpackPlugin()
  19. ],
  20. devServer: {
  21. port: 8082,
  22. hot: true,
  23. open: true
  24. }
  25. }

使用的地方用 import(yourImportName/module) 异步导入

  1. // app2/src/main.js
  2. const app1UtilsSayHello = async () => {
  3. const { sayHello } = await import("app1ImportName/utils");
  4. sayHello()
  5. }
  6. app1UtilsSayHello()

依赖共享

为了避免重复打包产物(例如loadash、react、vue等),Module Federation 提供了 shared 配置用于声明该应用可被共享的依赖模块:

两个应用都声明 shared 的模块才能被复用。

  1. // app1/webpack.config.js
  2. module.exports = {
  3. // ...
  4. plugins: [
  5. new MoudleFederationPlugin({
  6. name: "app1",
  7. filename: `remoteEntry.js`,
  8. exposes: {
  9. "./utils": "./src/uitls",
  10. "./foo": "./src/foo"
  11. },
  12. // 可被共享的依赖
  13. shared: ['lodash']
  14. })
  15. ]
  16. }
  1. // app2/webpack.config.js
  2. module.exports = {
  3. // ...
  4. plugins: [
  5. new MoudleFederationPlugin({
  6. remotes: {
  7. app1ImportName: "app1@http://localhost:8081/dist/remoteEntry.js"
  8. },
  9. // 可被共享的依赖
  10. shared: ['lodash']
  11. })
  12. ]
  13. }

上面的写法要求两个应用 shared 的模块版本一致才能复用;可以进一步用 shared.[lib].requiredVersion 写法来配置兼容的版本:

  1. module.exports = {
  2. // ...
  3. plugins: [
  4. new MoudleFederationPlugin({
  5. // ...
  6. shared: {
  7. lodash: {
  8. requriedVersion: "^4.17.0"
  9. }
  10. }
  11. })
  12. ]
  13. }

还可以通过 shared.[lib].shareScope 属性更精细地控制共享依赖模块范围:

  1. module.exports = {
  2. // ...
  3. plugins: [
  4. new MoudleFederationPlugin({
  5. // ...
  6. shared: {
  7. lodash: {
  8. sharedScope: "foo"
  9. }
  10. }
  11. })
  12. ]
  13. }

在这种配置下,其它应用所共享的 lodash 库必须同样声明为 foo 空间才能复用。shareScope 在多团队协作时能够切分出多个资源共享空间,降低依赖冲突的概率。

shared.singletong 还可以强制约束多个版本之间公用一个依赖包,如果不满足 shared.requiredVersion 则会抛出警告。

shared 其他配置

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注