公共模块之模块联邦

公共,模块,联邦 · 浏览次数 : 44

小编点评

**前言** 模块联邦是 Vue3 中一种用于将多个模块打包成一个可执行的模块的方法。它可以帮助减少页面加载时间,同时可以利用浏览器浏览器之间的共享机制。 **模块联邦的优点** * 简单易用:模块联邦提供了一个简单的 API,可以轻松地将多个模块打包成一个模块。 * 支持跨域:模块联邦可以跨不同域中的多个模块。 * 静态远程模块:模块联邦可以动态加载远程模块,这可以提高性能。 * 可扩展性:模块联邦可以被扩展以支持多种技术栈。 **模块联邦的缺点** * runtime 运行时性能影响:模块联邦对 runtime 运行时进行了一些改造,这会导致页面加载时间增加。 * 版本管理:模块联邦的 remote 地址是一个固定的地址,这可能导致问题在开发、测试、灰度和生产环境中。 **动态远程模块的解决方案** **方案一:通过插件将remotes地址解析为环境依赖的变量** 1. 使用 `process.env` 获取环境变量 `NODE_ENV`。 2. 如果 `NODE_ENV` 为 `develop`,则使用 `window.xinyu_version_test` 获取远程模块的地址。 3. 如果 `NODE_ENV` 为 `prod`,则使用 `window.xinyu_version_prod` 获取远程模块的地址。 4. 创建一个 `ExternalTemplateRemotesPlugin` 实例并配置其 `remotes` 属性。 5. 使用 `ExternalTemplateRemotesPlugin` 创建远程模块的模板。 **方案二:手动获取远程模块** 1. 使用 `fetch` 或 `axios` 请求远程模块的代码。 2. 将代码缓存到 `window` 对象中。 3. 在代码中使用 `import` 语句动态加载远程模块。 **选择模块联邦的方案** 模块联邦提供了多种选择,最适合您的项目取决于您的项目需求。如果您需要简单易用的模块联邦,则可以选择方案一。如果您需要更高的性能和控制,可以选择方案二。

正文

目录

前言

工作中公共模块通过子仓库在多个项目中使用,其中公共头部,登录,反馈、举报等模块业务与技术栈都和项目耦合很深,在每个项目都会将这些公共模块打包进去,为了减少流量成本,考虑将这些模块打包后放到cdn,对比了webpack中external、 dll、模块联邦方案,最终选择模块联邦。

external 方案 :external的场景是将三方模块当做外链引入, 依赖在公共模块和项目中无法共享;当然如果将依赖都通过external加载,可以解决共享问题,多版本同时存在的问题又无法解决。并且无法做到按需加载。

dll 方案:dll 使用场景是公共模块的处理,问题和external方案差不多

模块联邦概念

Container 容器
ModuleFederationPlugin 处理后打包出来的模块被称为 Container,可以加载其他的 Container,可以被其他的 Container 加载;

Host 宿主容器

消费其它容器的容器可以称为Host,一般动态去加载remote容器。

Remote 远程容器

被其他容器消费的容器可以称为Remote; 主要是导出模块供其他模块消费

因为Host和Remote都是Container,所以两者是相对的,一个Container既可以是Host,也可以是Remote;

Shared 共享

本地和远程可以共享的依赖

使用配置

Remote和Host使用同一个插件,在配置上有些许不同;

Remote例子:

new ModuleFederationPlugin({
  name: 'vue2App',
  filename: 'remoteEntry.js',
  library: { type: 'var', name: 'vue2App' },
  exposes: {
    './vue2': './node_modules/vue/dist/vue',
    './Button': './src/components/Button',
  },
  shared: {
    lodash: {
      strictVersion: true,
      requiredVersion: '^3',
    }
  }
}),

name字段是用来配置模块容器的名字,当用作被消费的容器的时候(remote) name字段是必需的。

filename 打包后产物的文件名,在host中配置的remotes字段中的文件链接就是这个文件。

exposes 指定需要导出的模块

library 指定打包产物的模块类型,在浏览器中使用一般配置为 { type: 'var', name: '自定义的全局变量' }

shared 指定需要共享的依赖模块,如果两个应用都使用了相同的依赖,则可以使用 shared 来共享依赖,减少资源加载量;支持使用semver规范配置兼容。

shared: [
  "lodash/",
  {
    react: {
      import: false,
      singleton: true
    }
  }
]

其中的requiredVersion使用Remote容器中配置的。 Host端可以配置version表示版本。

remotes 指定使用远程模块的别名和地址,地址的格式是固定的,@ 前面的名字(比如:vue2App) 需要和模块的library.name保持一致。Host端使用

new ModuleFederationPlugin({
    name: "main_app",
    remotes: {
        vue2App: 'vue2App@http://localhost:3001/remoteEntry.js'
    },
    shared: ['vue']
})

模块联邦优点

  1. 配置简单灵活

  2. 支持独立部署,上线

  3. 依赖共享机制;

  4. 容器可以嵌套使用,容器可以使用来自其他容器的模块。容器之间也可以循环依赖。

  5. 可以同步和异步方式都可以使用

    虽然vue2App/Button来自于remote,但是可以用同步的方式使用。

    import Vue2Button from 'vue2App/Button';
    console.log(Vue2Button)
    
    

    同样可以异步使用

    import('vue2App/Button').then(()=>{})
    

模块联邦缺点

  1. 模块联邦对runtime 运行时做了大量改造,会对我们页面的运行时性能造成一定的负面影响
  2. 需要额外处理远程模块的版本管理

动态远程模块

联邦模块的remote地址是一个固定的地址,实际开发是需要区分开发、测试、灰度、生产环境的,甚至需要区分版本,这时候怎么能让这个地址成为动态呢?

  1. 方案一, 通过插件将remotes地址将指定格式的表示式解析为环境依赖的变量,比如下面的[window.xinyu_version_prod]在页面执行的时候会取当前页面的window.xinyu_version_prod变量,所以如果能在页面脚本执行前把变量注入,就能实现动态加载远程模块。

    官方的demo中一个动态remote地址的例子,有人整理成了一个webpack插件

    const ExternalTemplateRemotesPlugin = require('external-remotes-plugin');
    
    const DynamicAddress = process.env.NODE_ENV === 'develop' 
      ? 'https://xxxxx/[window.xinyu_version_test]' : 'https://xxxxx/[window.xinyu_version_prod]';
    
    plugins:[
      new ModuleFederationPlugin({
        name: 'detail2022',
        remotes: {
          xinyu: `xinyu@${DynamicAddress}/remoteEntry.js`,
        },
      }),
      new ExternalTemplateRemotesPlugin(),
    
    ]
    
    
  2. 方案二 不通过remotes配置远程模块,而是手动获取远程模块,官方文档链接github例子链接

    通过手动加载remoteEntry.js之后,再通过webpack提供的方法获取模块,这样就可以在代码中动态加载。下面是一个例子

    function loadComponent(scope, module) {
      return async () => {
        // Initializes the share scope. This fills it with known provided modules from this build and all remotes
        await __webpack_init_sharing__('default');
        const container = window[scope]; // or get the container somewhere else
        // Initialize the container, it may provide shared modules
        await container.init(__webpack_share_scopes__.default);
        const factory = await window[scope].get(module);
        const Module = factory();
        return Module;
      };
    }
    
    function loadScript(url){
    
      return new Promise((resolve,reject)=>{
        const element = document.createElement('script');
    
        element.src = url;
        element.type = 'text/javascript';
        element.async = true;
    
        element.onload = () => {
          resolve()
        };
        document.body.appendChild(element);
      })
    
    }
    
    components: {
      Vue2Button: defineAsyncComponent(() => {
        return new Promise(async(resolve) => {
          await loadScript('http://localhost:3001/remoteEntry.js'); // 根据环境使用不同的地址,以做到动态获取模块
          const Button = await loadComponent('xinyu', './Button')();
          const vue2 = await loadComponent('xinyu', './vue2')();
          window.Vue2 = vue2;
          resolve(vue2ToVue3(Button.default, 'vue2Button'))
        })
      })
    },
    

    缺点:不能利用webpack对异步的同步处理,代码繁琐。

    import add from 'xinyu/add'; // 需要自己load代码,无法这样同步使用了
    
    add()
    
  3. 基于promise的动态远程模块,文档

    remote容器暴露的全局变量上只有这两个方法,通过代理的方式修改这两个方法,实现动态remote.

    该方案和external-remotes-plugin的使用效果差不多,都可以根据环境来动态切换远程模块。


与公共模块之模块联邦相似的内容:

公共模块之模块联邦

目录 前言 模块联邦概念 使用配置 模块联邦优点 模块联邦缺点 动态远程模块 前言 工作中公共模块通过子仓库在多个项目中使用,其中公共头部,登录,反馈、举报等模块业务与技术栈都和项目耦合很深,在每个项目都会将这些公共模块打包进去,为了减少流量成本,考虑将这些模块打包后放到cdn,对比了webpack

无业游民写的最后一个.net有关项目框架

理想很丰满,现实往往很残酷。 一种按照ddd的方式,根据业务来把自己需要的模块一个一个写出来,再按照模块把需要的接口一个一个的写出来,堆砌一些中间件,以及解耦的command,handler等等 ,一个项目就这么成型了。上面的项目有一个非常清晰的特点,就是按需开发,不需要去可以定义业务相关的公共的模

[大数据][机器学习]之Model Card(模型卡片)介绍

每当我们在公有云或者私有云发布训练好的大数据模型,为了方便大家辨识、理解和运用,参照huggingface所制定的标准制作一个Model Card展示页,是种非常好的模型展示和组织形式。 下面就是一个Model Card 的示例,我试着把它翻译成了中文,源网址,并且提供了Markdown的模板,供大

Sementic Kernel 案例之网梯科技在线教育

2023年4月25日,微软公布了2023年第一季度财报,营收528亿美元, 微软CEO纳德称,「世界上最先进的AI模型与世界上最通用的用户界面——自然语言——相结合,开创了一个新的计算时代。」该公司有近2500位Azure-OpenAI 服务客户,并称AI已被整合到多种产品中。 微软杀疯了!接入Ch

算法金 | LSTM 原作者带队,一个强大的算法模型杀回来了

大侠幸会,在下全网同名「算法金」 0 基础转 AI 上岸,多个算法赛 Top 「日更万日,让更多人享受智能乐趣」 时间拉回 2019 年,有「计算机界诺贝尔奖」之称图灵奖获得者公布,深度学习三巨头:Yoshua Bengio、Geoffrey Hinton、Yann LeCun 众望所归。 图灵奖为

LeetCode 周赛 345(2023/05/14)体验一题多解的算法之美

本文已收录到 AndroidFamily,技术和职场问题,请关注公众号 [彭旭锐] 提问。 往期回顾:LeetCode 双周赛第 104 场 · 流水的动态规划,铁打的结构化思考 周赛概览 T1. 找出转圈游戏输家(Easy) 标签:模拟、计数 T2. 相邻值的按位异或(Medium) 标签:模拟、

Linux下PAM认证详解(以centos7为例)

Linux下PAM认证详解(以centos7为例) PAM简介(Pluggable Authentication Modules,可插拔认证模块) Sun公司于1995年开发的一种与认证相关的通用框架机制:PAM(可插拔认证模块)是实现认证工作的一个模块。 因为每个服务都用到不同的认证方式,所以就需

[转帖]Spring体系结构:七大核心模块详解

https://www.toutiao.com/article/7088616970362487329/ spring是一个非常优秀的java框架,99%的公司都在使用,spring算是必备技能,所以一定要掌握好@mikechen Spring简介 Spring是一个基于控制反转IOC和面向切面编程

简单进行Springboot Beans归属模块单元的统计分析方法

简单进行Springboot Beans归属模块单元的统计分析方法 背景 基于Springboot的产品变的复杂之后 启动速度会越来越慢. 公司同事得出一个结论. beans 数量过多会导致启动速度逐渐变慢. 之前同事写过功能进行分析. 但是本着能不影响产品就不影响产品. 我想通过其他方式进行处理.

C#的基于.net framework的Dll模块编程(四) - 编程手把手系列文章

这次继续这个系列的介绍: 一、命名空间的起名; 对于C#来说,一般命名空间的建议是:公司名(或个人名称).产品名.分类名,比如我这边是用的这个:Lzhdim.LPF.Helper,意思是个人名Lzhdim,加上LPF为平台名,加上Helper分类为帮助类,其它的更长的请读者自己添加。 二、Dll库里