重磅:微前端与模块联邦项目落地

news2025/1/8 18:44:26

微前端概念及诞生背景

微前端的出现背景可以追溯到大型前端应用的开发和维护过程中所面临的挑战和问题。

  1. 大型应用的复杂性:随着前端应用规模的扩大,应用的复杂性也增加。大型应用通常由多个团队协同开发,每个团队负责一部分功能模块,但整合和部署变得困难,因为不同团队使用的技术栈、构建工具和开发风格可能不同。

  2. 高度耦合的架构:传统的单体应用架构通常采用紧耦合的模块依赖,即不同模块之间直接引用和依赖彼此。这样的架构使得代码的重用和独立部署变得困难,同时增加了代码的维护成本。

  3. 技术栈多样性:现代前端开发中存在着大量的技术栈和框架选择。不同的团队可能使用不同的技术栈来开发各自的模块,而传统的单体应用架构往往限制了团队的选择,导致技术栈的冲突和限制,理想状态是,当前一个模块可以有自己的技术选型,比如说A模块用vue2开发,B模块,是新模块,想使用当前流行的vue3开发,二C模块为了能基于开源项目二次开发,直接使用了react实现。

  4. 独立部署和快速迭代:快速交付和持续部署是现代应用开发的重要需求。然而,在传统的单体应用中,每次修改都需要重新部署整个应用,这不仅耗时,还会带来风险。同时,团队可能希望独立地迭代和部署自己的模块,以提高开发效率和灵活性。

基于以上的背景和需求,微前端架构应运而生。它旨在解决大型前端应用开发中的复杂性、耦合性、技术栈多样性和独立部署等问题。微前端将应用拆分为更小的、独立的模块,每个模块可以由不同的团队开发和部署,具有独立的技术栈和生命周期。这种架构使得团队可以更好地协作、复用代码、独立部署和快速迭代,同时提高了应用的可扩展性和可维护性。

公司项目应用拆分

当前我们有一个迭代了3年的巨型前端项目,是一个三百多个页面的单页面SPA应用。原来是基于vue-cli2构建的,后面升级为vue-cli3项目。每次冷启动应用需要不下10分钟,热更新一次大概要5s,而打包输出的包体快100M了,称它为shit mountain code(屎山代码)也不为过。当然这也不能全怪前端,当时的开发也不知道项目会有N多个迭代,变得这么庞大笨重。在一个契机下,公司需要将项目做改造,大概是基于应用场景,将应用分割为多个云产品,大体如下图所示:
应用图片
基于公司的业务发展,我们就将我们的前端应用进行了上图的应用拆分(基于公司业务拆分)。

但是这里慎重提醒各位码友,在改造之前可以看一下这篇文章,非必要不要改造

于是我们本来是一个web应用,突然变更了十几个前端应用的部署,前端直接骂娘了,本来我一个应用开发切分支搞定,现在要开十几个项目,每个项目都有自己的master, dev, feature分支,每天切项目,切分支都搞死人了,还经常弄错项目。运维本来从一个前端应用的jenkens部署,突然变成了十几个,而且每个应用都有自己的线上环境,开发环境,测试环境,运维直接撂挑子不干了。

一开始可谓是困难重重,微前端框架选型,我们使用的是@micro-zoe/micro-app方案。
@micro-zoe/micro-app 是一个基于 Custom Elements(Web Components)和 Shadow DOM 技术的微前端框架。它提供了一种轻量级的方式来实现前端应用的解耦和独立部署。

该框架的实现原理:

  1. Custom Elements(自定义元素)@micro-zoe/micro-app 利用浏览器原生的 Custom Elements API,将前端应用封装为独立的自定义元素(Custom Element)。自定义元素是一种浏览器支持的标准,允许开发者定义自己的 HTML 标签,具备类似于内置元素的行为和功能。

  2. Shadow DOM(影子 DOM):每个自定义元素都有一个关联的 Shadow DOM,它提供了一种隔离的 DOM 环境。通过使用 Shadow DOM,@micro-zoe/micro-app 可以确保微前端应用的样式和 DOM 结构与其他应用或页面相互隔离,避免冲突。

  3. 应用通信@micro-zoe/micro-app 提供了一套通信机制,使得不同的微前端应用之间可以进行跨框架的通信。它使用 CustomEvent API 和全局事件总线,允许应用之间进行事件的发布和订阅,以实现应用间的消息传递。

  4. 生命周期管理@micro-zoe/micro-app 通过监听自定义元素的生命周期事件(如 connectedCallback 和 disconnectedCallback),可以在应用加载和卸载时执行相应的操作。这使得应用可以在正确的时机进行初始化、启动和销毁,保证了整体应用的稳定性。

总的来说,@micro-zoe/micro-app 利用浏览器原生的 Custom Elements 和 Shadow DOM 技术,将前端应用封装为独立的自定义元素,并使用一套通信机制和生命周期管理来实现微前端应用之间的解耦和独立部署。它的轻量级设计和原生浏览器支持的特性使得它具有良好的性能和兼容性,并且可以与其他前端框架(如 React、Vue、Angular 等)进行集成。

整体结构图如下:
结构

经过内部的多轮讨论,最终没有选择qiankun,而且选择了micro-app。但是真是改造,真可谓坑巨多。主要集中在以下问题:

  1. 各种跨域,静态资源加载失败(因为子应用是基于父应用的域名来访问的)
  2. 子应用内存溢出,父子应用莫名其妙加载失败。
  3. 还有一些开发规范问题,某些页面是history模式,有些是hash模式,这就导致我们必须统一了。如果你的子应用是hash加载的,那你的子应用就不能有history模式的页面。
  4. 本地存储的问题,首先,拆分应用后,是共享当前基座应用下的本地存储的(localStorage,sessionStorage),那么子应用最好都用应用name来单独存储自己应用的key,不然会导致相互覆盖,主应用可以存一些公共的key,如:token,userInfo等。
  5. 基座应用样式影响子应用,子应用都是挂载到基座应用下面的,所以基座应用的全局样式会直接被子应用应用上。虽然官方提供了样式隔离的方案,但某些时候有缺陷。
  6. 子应用路由匹配不上,导致子应用进不去页面。

总体来说:改造成本还是很高的,如果非必要,不要轻易尝试。最终经过2个月的不懈努力,项目终于稳定上线了。

改造后无法忍受的坑

因为应用拆分,子应用相互隔离,与基座应用的通讯是基于变量,函数级别的通讯,导致一个问题,我们所有应用的公共组件无法复用,开始是拷贝了common-components给每个子应用都有一份,没觉得什么问题,但是开发维护一段时间后就有问题了。

  1. 因为公共组件随着业务发展也是要更新的,只更新当前应用,那其他应用就无法更新。
  2. 某些业务组件只在某个业务模块有,其他业务模块如果有相同的业务组件需要引用,就无法引用了,只能再拷贝,拷贝的组件又引用其他文件,拷贝也变得困难。

因为功能的组件,函数,我们都是copy的,导致我们的每个子应用还是臃肿不堪,极其冗余和不好维护。

痛苦不堪后模块联邦来了

Webpack 5 新增了一项功能–模块联邦(Module Federation),旨在解决前端微服务架构中的模块共享和应用集成问题。它使得不同的 Webpack 构建可以共享模块,从而实现了在不同的应用之间共享代码和资源的能力。

模块联邦的实现原理如下:

  1. 主应用(Host)和远程应用(Remote):在模块联邦中,存在一个主应用和一个或多个远程应用。主应用是整个应用的入口,而远程应用是提供独立功能的应用。

  2. 远程容器(Remote Container):每个远程应用都会创建一个远程容器,它负责加载和管理远程应用的模块。远程容器是一个独立的 Webpack 构建,它包含了远程应用的代码和资源,并将它们封装为可以动态加载的模块。

  3. 共享模块(Shared Module):模块联邦允许不同的应用共享模块。主应用和远程应用可以声明它们希望共享的模块,以及模块的版本和名称。这样,在构建过程中,Webpack 会将共享模块提取到一个独立的文件中,并在主应用和远程应用之间共享使用。

  4. 动态远程加载(Dynamic Remote Loading):模块联邦使得主应用可以在运行时动态加载远程应用的模块。主应用可以根据需要动态地加载远程应用的代码和资源,并在主应用中使用这些模块。

  5. 共享上下文(Shared Context):为了确保共享模块的正确性和一致性,模块联邦提供了共享上下文的概念。通过共享上下文,主应用和远程应用可以共享一些全局的状态和配置,以便共享模块的正确执行。

模块联邦使得不同的应用可以以独立的方式开发、构建和部署,同时可以共享和集成代码和资源。它提供了一种更灵活、松耦合的前端架构方式,有助于构建大型复杂应用和微服务体系结构。同时,模块联邦还提供了一些安全性措施,以确保远程应用和共享模块的安全性和可靠性。

正如上面所说,我们需要webpack5的模块联邦功能,将公共组件,函数,样式,package包等共享出来,供其他应用使用。大体结构图如下:
结构图
在上述流程图中,展示了一个微前端架构应用的结构和关系。基座应用A作为主应用(业务中台),提供公共组件、工具函数、公共样式(表示为"C")给其他所有子应用(物业云、能源云、设备云、资管云)。某些子应用也会提供业务组件(表示为"D1"、“D2”、“D3”、“D4”)给其他子应用使用。某些应用之间存在通讯关系(如B1和B2之间、B2和B3之间),但不能直接兄弟应用间通讯,而是先通讯给父组件(业务中台),再由业务中台发送事件给对应的子应用。

参考demo

下面提供一个@micro-zoe/micro-app微前端架构和模块联邦的应用demo:
运行效果

主应用(业务中台):

// 主应用的Webpack配置文件

const HtmlWebpackPlugin = require('html-webpack-plugin');
const { ModuleFederationPlugin } = require('webpack').container;

module.exports = {
  // 其他配置项...
  plugins: [
      new webpack.container.ModuleFederationPlugin({
        name: packageName,
        filename: 'remoteEntry.js',
        exposes: {
        // 暴露按钮组件,和工具函数
          './Button.vue': './src/components/Button.vue',
          './util.ts': './src/assets/util.ts'
        }
      })
    ]
};

sub-app1(物业云):

// 远程应用1的Webpack配置文件

const HtmlWebpackPlugin = require('html-webpack-plugin');
const { ModuleFederationPlugin } = require('webpack').container;

module.exports = {
  // 其他配置项...
  plugins: [
      new webpack.container.ModuleFederationPlugin({
        name: 'sub-app1',
        filename: 'remoteEntry.js',
        // 使用useDynamicScript,好处是更改后不用重启服务,也可以更灵活,甚至选择不同的远程版本来加载
        remotes: {
        },
        shared: {}
      })
    ]
};

sub-app2(资管云):

// 远程应用2的Webpack配置文件
module.exports = function override(config, env) {
    // 添加模块联邦配置
    config.plugins.push(
        new ModuleFederationPlugin({
            name: 'sub-app2',
            filename: 'remoteEntry.js'
        })
    );

    return config;
};

通过以上配置,我们在主应用(base)中可以使用@micro-zoe/micro-app来实现微前端架构,同时使用模块联邦来实现子应用的组件共享。具体代码如下:

// 主应用代码,启动微前端,注册子应用
import microApp from '@micro-zoe/micro-app'
microApp.start()

子应用react应用使用主应用公共函数:

// 加载远程模块
import remoteRef from "./remoteConfig/remoteRef";
useEffect(() => {
(async () => {
      const { util } = await remoteRef();
      // 检测函数,和防抖函数
      const {isElementInViewport, debounce} = util
  })();
})

完整的代码放到GitHub上了,个人在开发过程中,微前端和模块联邦问题巨多,特别是结合用的时候,也会有一些莫名其妙的问题。不过大部分问题都能在找到解决办法,大家如果遇到问题,可以在下面提问题,我看到了会尽力收集问题和提供解决思路:issues。

如果你觉得这篇文章对你有帮助,可以关注我的公众号:程序员每日三问。每天向你推送面试题,算法及干货,期待你的点赞和关注。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/682136.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

干货 | 智能网联车个人数据流通安全要求

以下内容整理自清华大学《数智安全与标准化》课程大作业期末报告同学的汇报内容。 我们对相关已有的标准规范进行调研,在工业和信息化部办公厅印发的《车联网网络安全和数据安全标准体系建设指南》中规定了六个部分的安全要求,我们做的主要是个人信息保护…

MaskFormer:将语义分割和实例分割作为同一任务进行训练

目标检测和实例分割是计算机视觉的基本任务,在从自动驾驶到医学成像的无数应用中发挥着关键作用。目标检测的传统方法中通常利用边界框技术进行对象定位,然后利用逐像素分类为这些本地化实例分配类。但是当处理同一类的重叠对象时,或者在每个…

c++11 标准模板(STL)(std::basic_streambuf)(五)

定义于头文件 <streambuf> template< class CharT, class Traits std::char_traits<CharT> > class basic_streambuf; 类 basic_streambuf 控制字符序列的输入与输出。它包含下列内容并提供到它们的访问&#xff1a; 1) 受控制字符序列&#xff…

【探索 Kubernetes|作业管理篇 系列 13】StatefulSet 拓扑状态

前言 大家好&#xff0c;我是秋意零。 在上一篇中&#xff0c;我们讲解了 Deployment 这种无状态的控制器&#xff1b;而如果要部署有状态的应用&#xff0c;那么 Deployment 显然是达不到我们的需求的。 今天讲解的是 StatefulSet 有状态控制器。 最近搞了一个扣扣群&…

电气照明节能设计在智能控制系统中的应用

【摘要】&#xff1a;随着社会的不断发展&#xff0c;经济水平也随之稳步提升&#xff0c;人们的消防意识也正在逐步加强&#xff0c;这就促进了消防应急照明系统的发展。当今社会,智能照明系统已经普及到家家户户&#xff0c;并在建筑工程中被广泛应用&#xff0c;同时其自身具…

【计算机网络自顶向下】计算机网络期末自测题(一)

前言 “(学不懂一点) &#xff08;阴暗的爬行&#xff09;&#xff08;尖叫&#xff09;&#xff08;扭曲&#xff09;&#xff08;阴暗的爬行&#xff09;&#xff08;尖叫&#xff09;&#xff08;扭曲&#xff09;&#xff08;阴暗的爬行&#xff09;&#xff08;尖叫&#…

非公平锁实现原理+源码解读

目录 非公平锁实现原理 加锁解锁流程 加锁源码 解锁源码 非公平锁实现原理 加锁解锁流程 先从构造器开始看&#xff0c;默认为非公平锁实现 public ReentrantLock() {sync new NonfairSync(); } NonfairSync 继承自 AQS 没有竞争时 第一个竞争出现时 Thread-1 执行了…

解决不允许一个用户使用一个以上用户名与一个服务器或共享资源的多重连接的问题

问题概述&#xff1a; 用windows server 2012 r2 vl x64搭了个文件服务器&#xff0c;在使用时有个问题&#xff0c;老是用户登录有问题&#xff0c;提示“不允许一个用户使用一个以上用户名与一个服务器或共享资源的多重连接”。出现的原因不详&#xff0c;网上也没查到合理的…

操作系统3:CPU任务调度和进程调度

目录 1、处理机调度的层次 &#xff08;1&#xff09;高级调度(High Level Scheduling) &#xff08;2&#xff09;低级调度(Low Level Scheduling) &#xff08;3&#xff09;中级调度(Intermediate Scheduling) 2、处理机调度算法的目标 批处理系统的目标 3、作业与作…

MongoDB 基于角色的访问控制

一、概述 MongoDB采用基于角色的访问控制&#xff08;RBAC&#xff09;来管理对 MongoDB系统。向用户授予一个或多个角色那 确定用户对数据库资源和操作的访问权限。外面 在角色分配中&#xff0c;用户无权访问系统。 1、启用访问控制 MongoDB默认情况下不启用访问控制。您可…

Facebook Insights分析工具解读,掌握关键数据指标

什么是Facebook Insights&#xff1f; Facebook Insights是Facebook平台上的一项内置分析工具&#xff0c;旨在帮助企业和品牌了解其在Facebook上的表现和受众互动情况。该工具提供了丰富的数据和指标&#xff0c;可以帮助用户洞察粉丝群体、了解发布内容的表现&#xff0c;并…

闭门造轮(LVGL_2)

例程1_// 组件(widgets)&#xff1a; 标签(label)的用法 static void label_event_cb(lv_event_t * e) {lv_obj_t * obj lv_event_get_target(e); // 获取触发事件的部件(对象)lv_event_code_t code lv_event_get_code(e); // 获取当前部件(对象)触发的事件代码sw…

Golang每日一练(leetDay0105) 最小高度树、戳气球

目录 310. 最小高度树 Minimum Height Trees &#x1f31f;&#x1f31f; 312. 戳气球 Burst Balloons &#x1f31f;&#x1f31f;&#x1f31f; &#x1f31f; 每日一练刷题专栏 &#x1f31f; Rust每日一练 专栏 Golang每日一练 专栏 Python每日一练 专栏 C/C每日一…

用自定义域名访问Tailscale节点

需求 tailscale 是好东西&#xff0c;在任何地方都可以和在局域网访问一样&#xff0c;但是也有着 IP 访问的不便&#xff0c;一方面 IP 是 tailscale 分配的&#xff08;非子网路由模式&#xff09;&#xff0c;另一方面还要记住各种端口 tailscale 也考虑到了这些问题&…

SpringBoot 使用 TestRestTemplate 进行 RESTful API 集成测试

SpringBoot 使用 TestRestTemplate 进行 RESTful API 集成测试 RESTful API 集成测试是测试应用程序与其外部依赖项之间的集成。SpringBoot提供了TestRestTemplate来测试RESTful API&#xff0c;本文将介绍如何使用TestRestTemplate进行RESTful API集成测试。 1. 什么是 TestR…

java File类 和 IO流

File类 文件和文件夹(文件路径)的抽象表示&#xff0c;是专门来出来磁盘上面的文件或文件夹的 构造方法 方法 返回boolean creatNewFile() 生成一个文件&#xff0c;当且仅当具有该名称的文件尚不存在时 public class Demo02 {public static void main(String[] args) th…

知了汇智网安项目实训助力高校实战型人才培养

当前&#xff0c;随着信息技术的高速发展和网络的普及应用&#xff0c;网络安全面临着日益复杂和严峻的挑战。恶意黑客、网络病毒、数据泄露等安全威胁不断涌现&#xff0c;给个人、企业和国家的信息资产和社会稳定带来了巨大风险。新形势之下&#xff0c;培养网络安全实战型人…

深眸科技受邀参加昆山元宇宙装备展与产业论坛,为工业视觉注智赋能

为落实《“十四五”数字经济发展规划》&#xff0c;进一步夯实元宇宙在终端装备领域的落地与拓展&#xff0c;江苏昆山将于6月27至29日&#xff0c;举办“2023元宇宙装备展”。本届展会由昆山市人民政府主办&#xff0c;昆山市工业和信息化局、赛迪工业和信息化研究院集团&…

力扣题库刷题笔记8--字符串转换正数(atoi)

1、题目如下&#xff1a; 2、个人Python代码实现如下&#xff1a; 这里可以看到&#xff0c;解答错误很多次&#xff0c;实际上就是对于题目的条件读的不够细&#xff0c;导致很多边界值的用例跑不过。而且个人习惯极其不好&#xff0c;没有输入各种可能的输入进行调试&#xf…

【计算机网络自顶向下】计算机网络期末自测题(一)答案

2019-2020 学年第 2 学期自测题答案及评分标准 (卷 1) 计算机网络 一、 填空题&#xff1a; 参考答案&#xff1a; 1 、 01000101 、11100111 3 、 100Mbps、双绞线、基带、全双工 [10Mbps 要求单位] 4 、 报文 5 、 ICMP 6 、 虚电路 7 、 距离矢量、链路状态 …