前言

之前使用京东微前端框架MicroApp集成10个微前端的页面到AngularJs的后台管理系统中,每个微前端做成一个菜单,一共10个,每次打开都是一个新的微前端,但是发现打开的微前端越多,容易造成内存泄露,下面讲解如何解决这个问题。

操作

之前的写法是每个angularjs页面如下所示:

<div class="border-left animated fadeInRight eee-bg-nano" style="border-left: 10px solid #e7eaec;" ng-controller="domainScenarioCtrl"ng-init="init('fireManage')"><div style="width: 100%; height: 100%;"><section style="height: 100%; width: 100%;" class="content" id="fireManage-p"><div style="height: 100%; width: 100%;" id="fireManage"><micro-app style="height: 100%; width: 100%;" name='fireManage'url="http://xxx/iot-front/fireListMagage"></micro-app></div></section></div>
</div>

这样的代码一共有10个文件,这种写法因为是写死的渲染所以容易造成内存泄露,经过询问ChatGpt以及反复验证和尝试,终于得到一个解决办法:

不使用标签,转为使用renderApp()方法动态控制url,每次打开一个tab时只渲染一个微前端,同时卸载其它微前端,也就是不管打开几个微前端页面,有且只有一个是激活状态!

在这里插入图片描述

同时需要解决以下几个场景问题:

1、点击菜单添加tab时激活新的微前端,卸载旧的

2、tab来回切换时激活当前新的微前端,卸载旧的

3、删除tab时激活当前新的微前端,卸载旧的

4、刷新页面时激活当前微前端

5、重复点击菜单tab时激活微前端,卸载旧的

针对以上思路和场景问题,我们一个个来看,首先,我们创建一个文件专门来处理微前端的操作:
micro-app-helper.js

(function (window) {'use strict';const MicroAppHelper = {getAppMap: function () {const context = window.getContext();return {'name1': 'url1',...};},// 处理微应用切换// app=url, alias = ''// global= state = null,sessionStoragehandleMicroAppSwitchByUrl: async function (global, app, isRedirect = false) {if (!app.url) return;try {const appMap = this.getAppMap();let appName = null, appUrl = null;for (const key in appMap) {if (app.url.includes(key)) {appName = key;appUrl = appMap[key];break;}}if (!appName || !appUrl) return;const activeApps = window.microApp.getActiveApps();const activeApp = activeApps.length > 0 ? activeApps[0] : null;const obj = {name: appName, url: appUrl, container: "#" + appName};// 卸载旧应用if (activeApp && activeApp !== appName) {console.log(`卸载旧微前端:${activeApp}`);await window.microApp.unmountApp(activeApp, {clearAliveState: true});const appContainer = document.querySelector('#' + activeApp);if (appContainer) {appContainer.innerHTML = '';console.log(`已移除 DOM 容器:${activeApp}`);}}// 是否需要跳转if (isRedirect) {if (app.alias){// 渲染新应用global.state.go(app.alias).then(() => {this.renderMicroApp(global, obj)});}} else {this.renderMicroApp(global, obj)}} catch (e) {console.error('微应用切换错误:', e);}},renderMicroApp: function (global, obj) {try {const userData = {currentUser: angular.copy(global.sessionStorage.currentUser),userSelOrg: '',selectOrg: angular.copy(global.sessionStorage.checkOrgInfo)};window.microApp.setData(obj.name, userData);window.microApp.renderApp(obj).then(() => {console.log(`${obj.name} 渲染完成`);});} catch (e) {console.log('renderMicroApp error:', e)}},getAppUrlByName: function (name) {const appMap = this.getAppMap();return appMap[name]}};// 暴露到全局window.MicroAppHelper = MicroAppHelper;
})(window);

上面代码中做了几件事:

1、卸载旧的微前端
2、渲染新的微前端
3、向微前端传值setData,注意对应好name,否则不生效
4、还有一些其它业务逻辑,比如angularjs中的$state.go()跳转页面方法,还有根据name匹配到微前端的url,最终在renderApp中做为参数执行,如果大家不需要的话,可以略过,我也懒的改了!

建好之后,我们在项目的index.html中引入这个js文件,否则不生效,如下所示:

<script type="module">// 在主应用中初始化if (!window.microAppInitialized) {import('./js/bundle.js').then((microApp) => {window.microApp = microApp.default || microApp;window.microAppInitialized = true;window.appList = []window.microApp.start({// iframe: true,destroy: true,delay: 0,preFetchApps: [{ name: 'buildingListManage', url: window.getContext().jiBaoUrl + 'iot-front/buildingManage' }, // 加载资源并解析],//预加载lifeCycles: {created(e, appName) {// console.log(`子应用${appName}被创建`)},mounted(e, appName) {console.log(`子应用${appName}已经渲染完成`)},unmount(e, appName) {console.log(`子应用${appName}已经卸载`)},},globalAssets: {js: ['http://xxx/iot-front/static/js/chunk-libs.91a68588.js','http://xxx/iot-front/jquery.min.js','http://xxx/iot-front/static/js/app.c49e13c0.js','http://xxx/iot-front/static/js/chunk-0f0d195a.483813fd.js']}});});}</script><script src="js/lib/microApp/index.js"></script>

bundle.js就是micro-app的源码,因为是angularjs项目,所以我直接这样写了,如果是vue和react的话,应该直接import就好!这里做了预加载以及初始化,同时把刚才的js文件引入!

1、点击菜单添加tab时激活新的微前端,卸载旧的
注意1和4虽然场景不同,但是效果一样,我们怎么处理呢,代码如下所示:

<div class="border-left animated fadeInRight eee-bg-nano" style="border-left: 10px solid #e7eaec;" ng-controller="domainScenarioCtrl"ng-init="init('fireManage')"><div style="width: 100%; height: 100%;"><div style="height: 100%; width: 100%;" id="fireManage"></div></div>
</div>

我们以这个页面做示例,我们调用init方法来渲染微前端页面,sceneName就是id名,要一致,否则不生效!

 $scope.init = function(sceneName) {MicroAppHelper.handleMicroAppSwitchByUrl({sessionStorage: $sessionStorage,state: $state,},{url: sceneName,alias: ''},false);})

这样不管是点击打开新的微前端还是刷新页面都会调用这个init方法,就能实时渲染微前端页面了!

2、tab来回切换时激活当前新的微前端,卸载旧的

注意:2、3、5我放在一起讲了,因为代码在一起。

因为我使用的是layui的tab组件,所以下面代码只有参考价值,毕竟大家应该都是用的新的UI技术了。

 $scope.initContent = function () {if (IBE.CONFIG.multiTab) {$scope.showMultiTab = true;let list = []//监听tab变化,不管是增加、删除还是点击,一律添加hash值layui.element.on('tab(contentab)', function (obj) {const thisUrl = $(this).attr("data-url");const thisAlias = $(this).attr('id');const alias = thisAlias.split('_')// 这个hash一定要加,否则打开新的tab不会被选中!location.hash = thisUrl;// 如果是已经打开过的,则直接激活if ($rootScope.isMenuTrigger){MicroAppHelper.handleMicroAppSwitchByUrl({sessionStorage: $sessionStorage,state: $state,},{url: thisUrl,alias: alias.join('.')},false);}})// 监听删除tab事件layui.element.on('tabDelete(contentab)', function (obj) {try{//获取删除后激活的tab元素const $tabs = $(obj.elem).find('li');  // 剩余的 lilet newActiveIndex = obj.index - 1;    // 删除前一个,通常就是新激活if(newActiveIndex < 0) newActiveIndex = 0;const $newActive = $($tabs[newActiveIndex]);const thisAlias = $newActive.attr('id');const thisUrl = $newActive.attr('data-url');if (!thisAlias || !thisUrl) returnconst alias = thisAlias.split('_')// 解决关闭tab时,url切换不成功问题location.hash = thisUrl;MicroAppHelper.handleMicroAppSwitchByUrl({sessionStorage: $sessionStorage,state: $state,},{url: thisUrl,alias: alias.join('.')},true);}catch(e){}})$(document).off('click', '.layui-tab[lay-filter="contentab"] .layui-tab-title li');// 使用自定义绑定和解绑click事件目的是为了防止事件被触发多次$(document).on('click', '.layui-tab[lay-filter="contentab"] .layui-tab-title li', async function () {const thisUrl = $(this).attr("data-url");const thisAlias = $(this).attr("id");const alias = thisAlias.split('_')MicroAppHelper.handleMicroAppSwitchByUrl({sessionStorage: $sessionStorage,state: $state,},{url: thisUrl,alias: alias.join('.')},true);});}}

上面一共三个事件tab(contentab)、tabDelete(contentab)和click,简单讲解下:
1、tab(contentab)事件:通过设置location.hash = thisUrl,将url改正确,并且通过isMenuTrigger来判断是否已经打开过也就是对应上面的第5条,如果是的话直接重新激活
2、tabDelete(contentab)事件:也一样激活新的,卸载旧的
3、click事件:和上面一样

这样就解决了所有场景的问题。

总结

1、因为我的主应用是angularjs和layui的tab所以需要处理的地方比较多
2、使用renderApp方式来动态加载微前端,不要使用micro-app标签
3、主子应用通过getData和setData来通信,注意name要匹配,否则不生效

引用

micro-app官方文档

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

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

线性代数 · 向量运算 | 叉乘 / 几何意义 / 推导

注&#xff1a;本文为 “线性代数 向量运算” 相关合辑。 图片清晰度受引文原图所限。 略作重排&#xff0c;未整理去重。 如有内容异常&#xff0c;请看原文。 数学基础 —— 向量运算&#xff08;叉乘&#xff09; keng_s 于 2016-08-05 17:17:57 发布 1_ 向量的叉乘 向量…

方法中只包含查询操作需要添加事务吗?

方法中只包含查询操作需要添加事务吗?绝大部分情况都不需要 是否需要为包含数据库查询操作的方法添加 @Transactional 注解,取决于业务需求和查询操作的特性,不能一概而论。以下是具体分析: 一、不需要添加 @Transactional 的常见场景 如果查询操作满足以下条件,通常不需…

MTK平台Wi-Fi学习--wifi channel 通过国家码进行功率限制和wifi eFEM 基本配置和wifi Tx SEM问题

一. 国家码可以用来限制功率上限,可以针对各国家实现By channel降功率的能力 可以通过country code来设置不同channel的power limit,操作方法如下: 在rlm_txpwr_init.h文件中g_rRlmPowerLimitConfiguration[]下添加需要限制功率的channel, 例如:国家码CN,信道:CH1,po…

MedGemma: 多模态医学文本与图像处理的创新模型

MedGemma: 多模态医学文本与图像处理的创新模型 今天&#xff0c;我有幸参加了在上海举行的Google 2025 I/O大会&#xff0c;这是一场充满创新与突破的技术盛宴。作为全球最具影响力的科技大会之一&#xff0c;Google I/O每年都会吸引来自世界各地的开发者、企业领袖以及科技爱…

深入剖析 C++ STL 中的 std::list 容器

基本介绍在 C 标准库&#xff08;STL&#xff09;中&#xff0c;std::list 是一个基于双向链表实现的序列容器。它与 std::vector、std::deque 等连续存储容器不同&#xff0c;提供了在序列中高效插入和删除元素的能力&#xff0c;尤其是在序列中间位置操作时优势明显。1. std:…

大规模调用淘宝商品详情 API 的分布式请求调度实践

在电商数据分析、比价系统、选品工具等业务场景中&#xff0c;往往需要大规模调用淘宝商品详情 API 以获取商品标题、价格、销量、评价等核心数据。然而&#xff0c;面对淘宝开放平台的严格限流策略、海量商品 ID 的处理需求以及系统高可用要求&#xff0c;传统的单节点调用方式…

在 Windows 系统中解决 Git 推送时出现的 Permission denied (publickey) 错误,请按照以下详细步骤操作:

完整解决方案步骤&#xff1a; 1. 检查并生成 SSH 密钥 # 打开 Git Bash ssh-keygen -t rsa -b 4096 -C "your_emailexample.com" 全程按回车&#xff08;使用默认路径&#xff0c;不设密码&#xff09; 密钥将生成在&#xff1a;C:\Users\<用户名>\.ssh\ 目…

【入门级-算法-2、入门算法:枚举法】

枚举法&#xff08;Brute Force&#xff09;&#xff1a;是一种直接遍历所有可能情况的算法思想&#xff0c;适合解决数据范围较小的问题。它的核心是穷举所有可能性&#xff0c;并检查哪些情况符合要求。 枚举法的基本思想&#xff1a;计算机主要功能&#xff0c;或者说它的优…

Python/Node.js 调用taobao API:构建实时商品详情数据采集服务

在电商数据分析、价格监控、竞品分析等场景中&#xff0c;实时获取商品详情数据至关重要。淘宝提供了丰富的 API 接口&#xff0c;允许开发者合法合规地获取商品信息。本文将介绍如何使用 Python 和 Node.js 两种主流语言调用淘宝 API&#xff0c;构建一个实时商品详情数据采集…

【OpenCV】Mat详解

在OpenCV中&#xff0c;cv::Mat是用于存储图像、矩阵等多维数据的核心数据结构&#xff0c;替代了早期的IplImage&#xff08;需手动管理内存&#xff09;&#xff0c;其设计的核心目标是自动内存管理和高效数据操作。下面详细介绍其组成原理及使用方法。 一、cv::Mat的组成原理…

疏老师-python训练营-Day45Tensorboard使用介绍

浙大疏锦行知识点回顾&#xff1a; tensorboard的发展历史和原理tensorboard的常见操作tensorboard在cifar上的实战&#xff1a;MLP和CNN模型 效果展示如下&#xff0c;很适合拿去组会汇报撑页数&#xff1a; 作业&#xff1a;对resnet18在cifar10上采用微调策略下&#xff0c;…

算法详细讲解:基础算法 - 离散化/区间合并

离散化 讲解 这里的离散化特指整数有序离散化。整个值域跨度很大&#xff0c;但是值非常稀疏的情况。 问题背景 我们有一个无限长的数轴&#xff0c;初始时每个位置上的值都是0。我们需要进行两种操作&#xff1a; 修改操作&#xff1a;在某个位置 x 上增加一个值 c。查询…

SpringBoot 实现在线查看内存对象拓扑图 —— 给 JVM 装上“透视眼”

0. 你将获得什么 一个可嵌入任何 Spring Boot 应用的内存对象拓扑服务&#xff1a;访问 /memviz.html 就能在浏览器看见对象图。 支持按类/包名过滤、按对象大小高亮、点击节点看详情。 线上可用&#xff1a;默认只在你点击“生成快照”时才工作&#xff1b;日常零开销。 1.…

STM32 HAL驱动MPU6050传感器

STM32 HAL驱动MPU6050传感器 项目概述 本项目实现了基于STM32 HAL库的MPU6050传感器驱动&#xff0c;可以读取加速度计和陀螺仪数据。项目使用I2C接口与MPU6050通信&#xff0c;并通过UART接口输出数据。 项目仓库地址&#xff1a;STM32_Sensor_Drives 硬件连接 MPU6050 I2…

flex-wrap子元素是否换行

flex-wrap设置子元素是否换行&#xff0c;默认情况下&#xff0c;项目都排在一条线&#xff08;又称”轴线”&#xff09;上。flex-wrap属性定义&#xff0c;flex布局中默认是不换行的。1、div的宽度是600px&#xff0c;每个span的宽度是150px&#xff0c;总共有5个&#xff0c…

RabbitMQ面试精讲 Day 21:Spring AMQP核心组件详解

【RabbitMQ面试精讲 Day 21】Spring AMQP核心组件详解 开篇 欢迎来到"RabbitMQ面试精讲"系列第21天&#xff01;今天我们将深入探讨Spring AMQP的核心组件&#xff0c;这是Java开发者集成RabbitMQ最常用的框架。掌握Spring AMQP不仅能提升开发效率&#xff0c;更是…

Flink TableAPI 按分钟统计数据量

一、环境版本环境版本Flink1.17.0Kafka2.12MySQL5.7.33二、MySQL建表脚本 create table user_log (id int auto_increment comment 主键primary key,uid int not null comment 用户id,event int not null comment 用户行为,logtime bigint null comment 日志时…

18.13 《3倍效率提升!Hugging Face datasets.map高级技巧实战指南》

3倍效率提升!Hugging Face datasets.map高级技巧实战指南 实战项目:使用 datasets.map 进行高级数据处理 在大模型训练过程中,数据预处理的质量直接决定了模型最终的表现。Hugging Face Datasets 库提供的 datasets.map 方法是处理复杂数据场景的瑞士军刀,本章将深入解析…

实体店获客新引擎:数据大集网如何破解传统门店引流难题

在商业竞争日益激烈的当下&#xff0c;实体店的生存与发展正面临前所未有的挑战。无论是街边的小型便利店&#xff0c;还是大型购物中心的连锁品牌&#xff0c;都在为"如何吸引顾客进店"而绞尽脑汁。传统广告投放效果不佳、线下流量持续萎缩、客户转化率难以提升………

LeetCode 分类刷题:2302. 统计得分小于 K 的子数组数目

题目一个数组的 分数 定义为数组之和 乘以 数组的长度。比方说&#xff0c;[1, 2, 3, 4, 5] 的分数为 (1 2 3 4 5) * 5 75 。给你一个正整数数组 nums 和一个整数 k &#xff0c;请你返回 nums 中分数 严格小于 k 的 非空整数子数组数目。子数组 是数组中的一个连续元素序…