• webpack高级应用篇(十五):基于 Promise 的动态 Remote,让模块联邦版本化



    静态 Remote 的问题

    webpack高级应用篇(十三):模块联邦(Module Federation)- 未来组件包更新解决方案 最后的结语中有提到这样一句话:

    试想一下,你有一个组件包通过npm发布后,你的10个业务项目引用这个组件包。当这个组件包更新了版本,你的10个项目想要使用最新功能就必须一一升级版本、编译打包、部署,这很繁琐。但是模块联邦让组件包利用CDN的方式共享给其他项目,这样一来,当你到组件包更新了,你的10个项目中的组件也自然更新了。是不是很香(*^▽^*)


    事实上,我们在享受即时更新,减少繁琐的升级包版本、编译打包、部署的同时,也丢失了组件的版本化。这样会给项目带来风险,试想一下,如果你的一个组件有比较大的改动,当这个组件通过模块联邦暴露出去并被多个项目引用,那么这些项目都是存在风险的。因为你也许并没有进行对应项目线下测试,这些项目通过模块联邦得到的组件就被自动更新了,这很危险。


    那么,有没有一个能解决版本化的方案呢?答案是肯定的。


    基于 Promise 的动态 Remote

    一般来说,remote 是使用 URL 配置的,示例如下:

    module.exports = {
      plugins: [
        new ModuleFederationPlugin({
          name: 'app1',
          remotes: {
            App2: 'app2@http://localhost:3001/App2RemoteEntry.js',
          },
        }),
      ],
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    我们可以向 remote 传递一个 promise,其会在运行时被调用。使用任何符合 get/init 接口的模块来调用这个 promise。例如,如果你想传递你应该使用哪个版本的联邦模块,你可以通过一个查询参数做以下事情:


    APP1

    配置

    webpack.config.js

    const HtmlWebpackPlugin = require('html-webpack-plugin');
    const { ModuleFederationPlugin } = require('webpack').container;
    
    module.exports = {
      mode: 'development',
      entry: './src/index.js',
      output: {
        clean: true,
      },
      devtool: false,
      devServer: {
        port: '3000',
        client: {
          logging: 'none',
        },
      },
      plugins: [
        new HtmlWebpackPlugin(),
    
        new ModuleFederationPlugin({
          name: 'app1',
          remotes: {
            App3: `promise new Promise(resolve => {
              // TODO
              // 可以在这里设计和实现你的 模块联邦 版本化,这里简单的从URL获取version
              const urlParams = new URLSearchParams(window.location.search)
              const version = urlParams.get('app2VersionParam')
              console.log('version', version);
              const remoteUrlWithVersion = 'http://localhost:3001/' + version + '/App2RemoteEntry.js'
              console.log('remoteUrlWithVersion', remoteUrlWithVersion);
              const script = document.createElement('script')
              script.src = remoteUrlWithVersion
              script.onload = () => {
                // 注入的脚本已经加载并在window上可用
                // 我们现在可以解析这个Promise
                const proxy = {
                  get: (request) => {
                    console.log('request', request);
                    return window.app2.get(request)
                  },
                  init: (arg) => {
                    try {
                      console.log('arg', arg);
                      return window.app2.init(arg)
                    } catch(e) {
                      console.log('remote container already initialized')
                    }
                  }
                }
                resolve(proxy)
              }
              // 将script的src设置为版本化的remoteEntry.js
              document.head.appendChild(script);
            })
           `,
          },
        }),
      ],
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59

    请注意当使用该 API 时,你 必须 resolve 一个包含 get/init API 的对象。


    打包后

    执行webpack

    打包编译后如下,是可以在运行时执行的,因此你可以考虑将获取version的方式改为通过接口获取
    在这里插入图片描述


    APP2

    配置

    webpack.config.js

    在发布前修改 filename

    const HtmlWebpackPlugin = require('html-webpack-plugin');
    const { ModuleFederationPlugin } = require('webpack').container;
    
    module.exports = {
      mode: 'development',
      entry: './src/index.js',
      output: {
        clean: true,
      },
      devtool: false,
      devServer: {
        port: '3001',
        client: {
          logging: 'none',
        },
      },
      plugins: [
        new HtmlWebpackPlugin(),
    
        new ModuleFederationPlugin({
          // 模块联邦名字,提供给其他模块使用
          name: 'app2',
          // 提供给外部访问的资源入口
          filename: '1.0.0/App2RemoteEntry.js', // 在这里修改版本号
          // 引用的外部资源列表
          remotes: {},
          // 暴露给外部的资源列表
          exposes: {
            /**
             *  ./Header 是让外部应用的使用时基于这个路径拼接引用路径,如:nav/Header
             *  ./src/Header.js 是当前应用的要暴露给外部的资源模块路径
             */
            './Header': './src/Header.js',
          },
          // 共享模块,值当前被 exposes 的模块需要使用的共享模块,如lodash
          shared: {},
        }),
      ],
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    打包后

    执行webpack,会得到新的文件

    在这里插入图片描述


    http://localhost:3000/?app2VersionParam=1.0.0

    在这里插入图片描述


    源码:https://gitee.com/yanhuakang/webpack-demos/tree/master/advanced/step_15-Module-Feder-Dynamic-Remote

  • 相关阅读:
    vue事件处理
    OpenCV--图像的基本变换
    众多mock工具,这一次我选对了
    PostgreSQL按月计算每天值的累加
    Nginx监控与告警:确保服务稳定运行
    #POWERBI_指标监控(第二部分,周期内下降天数及日期明细)
    C语言解决约瑟夫环问题
    性能测试-CPU性能分析,IO密集导致系统负载高
    详解数仓的向量化执行引擎
    [LeetCode解题报告] 1610. 可见点的最大数目
  • 原文地址:https://blog.csdn.net/qq_41887214/article/details/125962086