微前端-解決MicroApp微前端內存泄露問題

前言

之前使用京東微前端框架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 的 非空整數子數組數目。子數組 是數組中的一個連續元素序…