当前位置: 首页 > news >正文

[qiankun]-多页签缓存

[qiankun]-多页签缓存

  • 环境
  • 功能需求
  • 多页签缓存方案
    • 方案1.主服务进行html替换
    • 方案2.微服务vnode 替换
    • 方案3.每个微服务都不卸载
      • 微服务加载方式的选择
      • 微服务的路由路径选择
      • 微服务的缓存工具
      • 微服务的容器
        • 使用tab作为微服务的挂载容器
        • 使用微服务路由作为微服务的挂载容器
          • 场景描述
      • 微服务的缓存
        • 缓存微服务
        • 删除微服务
        • 不同路由同一微服务情况

管理系统比较常用的一个功能就时多页签的缓存,我们通过缓存已经打开的页签,并在切换页签的时候看到之前的查询结果,在关闭页签的重新点击菜单那时,看到的是新的没有查询记录界面

环境

语言:采用的主流的vue3.x+ts开发
UI框架:使用的是ant-design框架
其它工具:使用了微服务技术-qiankun
缓存工具:keep-alive

功能需求

  • 通过点击菜单,打开一个菜单页签(点击后展示的菜单称之为页签):

    如果点击菜单之前已打开页签中没有该菜单,则该页面应该是初始页面状态;

    如果点击菜单之前已打开页签中有该菜单,则该页面应该是上次操作后状态;

  • 切换已打开的页签:

    则页面应该都是上次操作后状态;

  • 关闭页签后再次点击菜单:

    因为点击菜单之前关闭页签,所以已打开页签中没有该菜单,则该页面应该是初始页面状态;

多页签缓存方案

方案1.主服务进行html替换

通过存储每个微服务容器的html内容,然后在切回当前页签的时候,微服务容器使用缓存的html替换,也就是所谓的替换innerHTML。

方案尝试结果描述:
替换的内容,使得切换回原有页面确实是上次的查询结果页面,但是页面失去了响应性,变成了静态的HTML页面,包括下拉框都没有了

方案2.微服务vnode 替换

通过存储每个微服务容器的 vnode 内容,然后在切回当前页签的时候,创建微服务实例的时候,使用vnode替换掉原有的render渲染函数

方案尝试结果描述:
因为本人使用的vue3开发,使用render渲染函数是vue2创建vue实例的方法,因此该方案无法实现使用vnode替换掉原有的render渲染函数

尝试了vnode的缓存强制替换的可能性,vue3提示_vnode是只读属性不可以设置

方案3.每个微服务都不卸载

以上两个方案,都是基于同时只加载一个微服务,并且在切换的时候卸载当前微服务,之所以先尝试前两个方案也是因为,如果页面每个微服务都不卸载,当打开的页签过多时会有性能问题,因为微服务总数量暂时有限,该问题此时先不考虑了,如果之后有需要会另外总结

因为前两个方案都不可行,只能尝试该方案,方案描述:

微服务加载方式的选择

qiankun加载加载微服务的方式有两种:

  1. 是注册加载,通过劫持路由,会根据路由变化加载每个微服务
    registerMicroApps,是根据路由的变化加载微服务的,只要路由变化,就会触发微服务重新加载微服务,因此无法阻止微服务的重新加载,缓存不能被使用,指定容器的内容会被覆盖,因此感觉不适合缓存页签的方案
  2. 是手动加载,可以自己决定什么时候加载
    loadMicroApp ,是手动加载,因此能决定是重新加载,还是使用缓存,感觉适合使用该方式

因此选择手动加载微服务的方式

微服务的路由路径选择

主服务具有公共页面,例如404,intro等路由页面,公共页面与微服务都是展示在页面的主体区,并且不需要缓存,因为没有操作功能,当然缓存也是可以的。微服务展示的时候,公共路由页面不可展示,路由也需要变为微服务的路由,不能使用之前的路由路径。

因此可以使用router.push(),或者router.replace()改变浏览器的路由路径,有说可以把路由router想象成一个访问记录的栈,router.replace() 是替换掉栈顶,而router.push() 则是向栈中再堆入一个新记录

考虑过所有微服务使用同一个路由路径,但是因为浏览器的前进后退记录管理服务,因此每个微服务的路由路径不能完全相同,否则无法区分微服务的前进后退记录,所以也不能使用replace方法了

因此每个微服务页面具有一个唯一的路由路径,需要使用router.push记录所有的路由路径,并且需要用来加载微服务的路由组件(如果没有找到指定的路由,系统会跳转404,因此需要微服务的路由组件,微服务众多,因此所有微服务可以采用相同的前缀,用以匹配到同一个路由组件),一个微服务路由组件就能够满足需求

相同的前缀,/micro/XXXX/XXXX,例如:/micro/service/function

{path: "/micro/:pathMatch(.*)*",///micro/:projectName/:pagemeta: { auth: true },component: () => import(/* webpackChunkName: "micro" */ "../micro/index.vue")}

微服务的缓存工具

默认情况下,一个组件实例在被替换掉后会被销毁。这会导致它丢失其中所有已变化的状态 —— 当这个组件再一次被显示时,会创建一个只带有初始状态的新实例。

keep-alive 是 vue 内置的动态组件缓存工具

keep-alive 缓存路由的key默认是每个路由的name,一般的路由缓存因为每个路由设置了不同的name,所以include包含需要缓存的路由的name即可

那么针对想更改缓存Key的情况:

<router-view v-slot="{ Component, route }"><keep-alive :include="cachePannels"><component :is="wrap(route, Component)" :key="wrapKey(route.path)" /></keep-alive>
</router-view>

以下对component与key的处理,PROJECT.cacheKey设置缓存的key字段:

const wrap = (route, component) => {component.type.name = "path" == PROJECT.cacheKey ? route.path : store.state.pathMap[route.path].id;return component
};
const wrapKey = (path) => {if ("path" == PROJECT.cacheKey) {return path} else {return store.state.pathMap[path].id}
}

经过测试发现若缓存的字段是path,则缓存成功,若是key是改成id则缓存不成功,因为对于源码的理解并不深入,所以目前并不理解原因,也暂时不做深入,这里还是想吐槽一句,看到在源码处这个问题已经被提出几年了,但是都没有一个官方正式的回应,都是小伙伴提供的各种解决方案,而且就像上面说的绑定的key还必须是path其它不可以。。。

好吧,暂时不纠结了,既然path可用,就确定使用path,以下是最终代码

<router-view v-slot="{ Component, route }"><keep-alive :include="cachePannels"><component :is="wrap(route, Component)" :key="route.path" /></keep-alive>
</router-view>
const wrap = (route, component) => {component.type.name = route.path;return component
};

因为微服务使用的是同一个路由组件,因此name相同,那么正好需要使用根据key进行缓存的功能

基于上述原因,使用keep-alive缓存,路由path,作为缓存的key。

关于keep-alive重写的功能以后有机会在尝试吧

微服务的容器

一开始我是倾向于直接用微服务路由组件作为加载微服务的容器

但是使用路由的path作为路由缓存的key,理论讲一旦path发生变更,则路由组件会被销毁重新加载,但是微服务切换时到底组件是否会被销毁?在尝试时存在一些无可避免的问题,当时没有成功

使用tab作为微服务的挂载容器

这里先讲述一个成功缓存的方式

<template>
<a-tabs  v-model:activeKey="activeKey"@tabClick="clickTab"  @edit="closeTab"><a-tab-pane v-for="item in cacheList" :key="item.id" :tab="item.title"><div :id="'micro'+item.id" class="micro-tab"></div></a-tab-pane>
</a-tab><router-view v-slot="{ Component, route }"><keep-alive :include="cachePannels"><component :is="wrap(route, Component)" :key="route.path" /></keep-alive>
</router-view>
</template>

微服务路由 micro.vue

<template>
</template>
<script lang="ts" setup>
import { useStore } from "store";
const store = useStore();
store.commit("loadMicroApp");
</script>

注意,虽然微服务加载是在tab中挂载的,但是router-view此时是要正常加载微服务路由的,因为路径要有对应的匹配路由,但是微服务又不在其中挂载,所以隐藏微服务路由 micro.vue 的展示即可

使用微服务路由作为微服务的挂载容器

加载的时候,因为使用的path作为key,理论来讲,keep-alive应该将每个微服务作为一个单独的路由进行缓存.

但是实际情况有所不同,现象描述:

在打开页签的过程中,每次都是onMount了的,切换的时候,可以看到确实缓存了微服务,每次切换的时候显示的是缓存页面

但是在关闭页签的时候出现了问题,一旦关闭了其中一个正在展示的微服务,其它微服务页面就白屏了,就像是其它路由组件都被删除了?!所以为什么作为单独路由缓存的其它页面没有正常展示?但是如果关闭的是没有展示的页签,则并不影响当前路由的展示与后续的切换

因为该问题没有解决,所以最终没有采用该方式!!下面采用路由组件作为容器时的现象

场景描述

主服务首次点击菜单A,打开页签A,挂载A服务 onMounted onActivated

主服务首次点击菜单B,打开页签B,挂载B服务 onMounted onActivated

切换A服务,B onDeactivated,A onActivated

切换B服务,A onDeactivated,B onActivated

此时关闭B服务:

实际:B onUnmounted,A onMounted onActivated

理论:B onUnmounted,A onActivated

这就像是A挂载到了B的路由组件上了?
正常的切换时keep-alive缓存的组件

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-61K3ycOm-1677045679175)(2023-01-03-11-37-19.png)]

vue 的组件,不同路径,同一个路由, 缓存的组件为同一个

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7wZSirDR-1677045679177)(2023-01-03-15-16-52.png)]

在这里插入图片描述

因此同组件路由卸载的时候,另外的就无法展示了,原因找到了,但是如何区分开?尝试过使用cloneVnode 克隆micro.vue组件,但是无效,所以暂时未找到解决方案

微服务的缓存

缓存微服务

需要缓存的微服务正常手动加载微服务即可,并缓存该微服务

最早的时候是直接缓存的

cacheMap[microApp.path] = loadMicroApp(microApp)

后来在实践中发现直接缓存是存在问题的,例如当loadMicroApp失败的时候(微服务不存在,不可用的情况),此时缓存的微服务实例是不存在的,自然对于该微服务实例后续的卸载会导致报错

因此,当缓存微服务的时候需要确保微服务加载成功:

const appMicro = loadMicroApp(microApp, { singular: false }, microLifeCycle);
appMicro.mountPromise.then((res) => {//微服务加载成功时保存微服务state.micro.cacheMap[microApp.path] = appMicro;
}).catch((error) => {//微服务加载失败时提示错误console.error(`mountPromise error`, error)
})

microLifeCycle是微服务的生命周期,可以在其中设置微服务的加载动画

删除微服务

关闭页签,也就是删除缓存微服务时,注意一定要先卸载微服务,unmount,否则下次加载该微服务时会报已挂载错误

//卸载微服务
cacheMap[microApp.path].unmount();
//删除微服务实例
delete cacheMap[microApp.path]

不同路由同一微服务情况

如果微服务有两个路由,而两个路由页面都点击后要缓存,此时也是正常加载即可,当作两个不同微服务加载即可,也不会报已挂载错误,因为路径不同可以当作缓存的是两个微服务(不同于在micro.vue中挂载,该路由组件中如果当作两个微服务挂载是会报已挂载错误的),因为是加载在tab中,彼此并不互相影响

相关文章:

[qiankun]-多页签缓存

[qiankun]-多页签缓存环境功能需求多页签缓存方案方案1.主服务进行html替换方案2.微服务vnode 替换方案3.每个微服务都不卸载微服务加载方式的选择微服务的路由路径选择微服务的缓存工具微服务的容器使用tab作为微服务的挂载容器使用微服务路由作为微服务的挂载容器场景描述微服…...

2|电子技术|数字电子技术基础|雨课堂习题|考前回顾

A/DD/A转化横向与阵列 相乘&#xff0c;竖向为或阵列 相加&#xff01;功率放大电路克服交越失真&#xff0c;是在乙类的基础上增加两个二极管&#xff0c;使微导通&#xff0c;使三极管导通时间大于半个周期&#xff0c;小于一个周期&#xff0c;构成甲乙类工作状态。选择填空…...

vue+echarts:圆形柱状图设置角度和最大值

第020个点击查看专栏目录本示例是显示圆形的柱状图&#xff0c;angleAxis设置一个max&#xff0c; angleAxis上startAngle&#xff1a;90 &#xff0c; 将0点设置为最顶点。 文章目录示例效果示例源代码&#xff08;共100行&#xff09;相关资料参考专栏介绍示例效果 示例源代码…...

Linux系统安装Nginx常见报错问题

安装Nginx从nginx官网下载所需版本的nginx&#xff0c;http://nginx.org/下载之后&#xff0c;将安装包上传到linux系统指定路径解压文件&#xff0c;tar -zxvf nginx-1.22.1.tar.gz &#xff08;此处用1.22.1版本为例&#xff09;进入安装包目录&#xff0c;cd nginx-1.22.1执…...

按下按键之后,打印一句话------>三个按键需要实现

main.c: #include "key.h" extern void printf(const char *fmt, ...); void delay_ms(int ms){ int i,j; for(i 0; i < ms;i) for (j 0; j < 1800; j);} int main(){ //key1键盘 //EXIT控制器初始化 void PF9_exti_init(); //GICD控…...

Mac配置VScode

Mac配置VScode 常用技巧 命令调色板 根据您当前的上下文访问所有可用的命令。 键盘快捷键&#xff1a;⇧⌘P 快速打开 快速打开文件。 键盘快捷键&#xff1a;⌘P **提示&#xff1a;**类型&#xff1f;查看命令建议。 在最近打开的文件夹和工作区之间导航 最近打开 键盘快捷…...

MAC地址IP地址 端口

网络结构&#xff1a; 服务器-客户机&#xff08;C/S&#xff09;Client-Server结构&#xff0c;如QQ,LOL都拥有客户端 优点&#xff1a;响应速度快&#xff0c;形式多样&#xff0c;安全新较高缺点&#xff1a;安装软件和维护&#xff0c;不能跨平台LINUX/windows/MAC浏览器-…...

关于虚拟数字人你想知道的都在这里

2022年底&#xff0c;微软旗下的人工智能实验室Open AI发布的对话式大型语言模型ChatGPT聊天机器人一夜蹿红&#xff0c;5天用户量超百万&#xff0c;在各大中外媒体平台掀起了一阵热潮。也带火了人工智能相关产业&#xff0c;AI虚拟数字人就是其中之一&#xff0c;一个随着元宇…...

分布式任务调度处理方案(无代码)

业务涉及到&#xff0c;需要向数据库、redis、elasticsearch、MinIO写四份数据&#xff0c;这里存在分布式事务问题。如何解决问题&#xff0c;先分析cap&#xff0c;是要保证可用性&#xff0c;还是保证一致性。如何选择是CP还是AP&#xff1f;分析业务场景CP的场景&#xff1…...

2023年博管办香江学者计划、澳门青年学者开始申报

2023年2月20日&#xff0c;全国博士后管委会办公室官方网站发出了2023年香江学者计划、澳门青年学者计划和博士后国&#xff08;境&#xff09;外学术交流项目申报指南&#xff0c;以下知识人网小编仅转载香江学者计划和澳门青年学者计划申报指南并做重点解读。知识人网整理香江…...

(二十一)、实现评论功能(1)【uniapp+uinicloud多用户社区博客实战项目(完整开发文档-从零到完整项目)】

1&#xff0c;评论回复模块的样式布局 1.1 在detail页面添加uview中的 Empty 内容为空组件 <!-- 评论区 --><view class"comment"><u-empty mode"comment" icon"http://cdn.uviewui.com/uview/empty/comment.png"></u-emp…...

【Docker】初识Dcoker以及镜像操作(一)

目录 1.初识Docker 1.1.什么是Docker 1.1.1.应用部署的环境问题 1.1.2.Docker解决依赖兼容问题 1.1.3.Docker解决操作系统环境差异 1.1.4.小结 1.2.Docker和虚拟机的区别 1.3.Docker架构 1.3.1.镜像和容器 1.3.2.DockerHub 1.3.3.Docker架构 1.3.4.小结 1.4.安装D…...

(1)C#传智:在vs2022中基本了解(第一天)

开始vs2022中C#入门&#xff0c;就是一笔记&#xff0c;算不上原创&#xff0c;没办法得选啊。 一、vs中卸载项目和移除项目有什么区别&#xff1f; 1、卸载、移除都不会移除物理文件&#xff0c;只会删除关联 2、卸载删除关联的程度低&#xff0c;卸载后项目只是“变灰色…...

【数据结构与算法】算法的时间复杂度和空间复杂度

文章目录前言1.算法效率1.1.如何衡量一个算法的好坏1.2.算法的复杂度2.时间复杂度2.1.时间复杂度的概念2.2.大O的渐进表示法2.3.常见时间复杂度计算举例2.4.常见时间复杂度3.空间复杂度4.复杂度oj练习Practice.1 消失的数字Practice.2 旋转数组写在最后前言 关于时空复杂度的分…...

不使用contab -e的方式,添加计划任务

不使用contab -e的方式&#xff0c;添加计划任务 crond 服务的周期任务的文件存放位置在&#xff1a;/var/spool/cron/ 如果你是root用户的话那么你的周期任务文件名就叫root 如果你使用其他用户创建的周期任务&#xff0c;任务文件名就叫它本身 1、 使用root用户创建周期任…...

sentry2摄像头之blink篇

一、硬件 arduino sentry2摄像头 二、实验内容 第一步 安装好esp8266库函数 具体详见ES826安装指导,CSDN有很多资源,或者浏览 https://tosee.readthedocs.io/zh/latest/ 网址 第二步 配置 详情见视频,有简单讲解 视频1:电脑端配置 https://live.csdn.net/v/277427 视频2:s…...

springboot集成PDF导出

内容目录 知识准备 什么是itext itext的历史版本和License问题 标准的itextpdf导出的步骤 实现案例 Pom依赖 导出PDF 添加页眉页脚和水印 进一步理解 遇到license问题怎么办 为何添加页眉页脚和水印是通过PdfPageEvent来完成 除了处理word, excel等文件外&#xff0c;最为常见的…...

Podman 创建持久 MySQL 数据库容器

使用正确的 SELinux 上下文和权限创建目录/home/student/local/mysql。 创建/home/student/local/mysql目录。 [studentworkstation ~]$ mkdir -vp /home/student/local/mysql mkdir: 创建的目录/home/student/local mkdir: 创建的目录/home/student/local/mysql/home/studen…...

Java-反射

反射概述 Java反射机制&#xff1a; 是指在运行时去获取一个类的变量和方法信息。然后通过获取的信息来创建对象&#xff0c;调用方法的一种机制。由于这种<动态性>&#xff0c;可以极大的增强程序的灵活性&#xff0c;程序不用在编译期就完成确定&#xff0c;在运行期仍…...

构造agent类型的内存马(内存马系列篇十三)

写在前面 前面我们对JAVA中的Agent技术进行了简单的学习&#xff0c;学习前面的Agent技术是为了给这篇Agent内存马的实现做出铺垫&#xff0c;接下来我们就来看看Agent内存马的实现。 这是内存马系列篇的第十三篇了。 环境搭建 我这里就使用Springboot来搭建一个简单的漏洞…...

JavaEE简单示例——<select>中的查询参数传递和结果集封装自动映射关系

简单介绍&#xff1a; 在之前我们在讲SQL映射文件中的映射查询语句的<select>标签的时候&#xff0c;对其中的四个常用属性的讲解并不是那么的透彻&#xff0c;今天就来详细的解释<select>的四个常用属性的具体含义以及<select>标签在进行查询的时候查询参数…...

信息安全圈都在谈论CISP,CISSP,这两者有什么区别呢?

CISP 和 CISSP 都是信息安全认证资格考试&#xff0c;但是它们之间有一些区别。 CISP&#xff08;Certified Information Security Professional&#xff09;认证考试是由国际信息系统安全认证联盟&#xff08;ISC)所开发和管理的&#xff0c;主要考核信息安全专业人员在保障企…...

浅谈Redisson实现分布式锁的原理

1.Redisson简介 Redis 是最流行的 NoSQL 数据库解决方案之一&#xff0c;而 Java 是世界上最流行&#xff08;注意&#xff0c;我没有说“最好”&#xff09;的编程语言之一。虽然两者看起来很自然地在一起“工作”&#xff0c;但是要知道&#xff0c;Redis 其实并没有对 Java…...

UVM实战(张强)-- UVM中的寄存器模型

目录一.整体的设计结构图二.各个组件代码详解2.1 DUT2.2 bus_driver2.3 bus_sequencer2.4 bus_monitor2.5 bus_agent2.6 bus_transaction2.7 bus_if2.8 my_if2.9 my_transaction2.10 my_sequencer2.11 my_driver2.12 my_monitor2.13 my_agent2.14 my_scoreboard2.15 my_env2.16…...

什么是 CSAT?这份客户满意度流程指南请查收

什么是 CSAT&#xff1f;如何计算我的客户满意度分数&#xff1f;大中型公司应该熟悉这些术语。以下文章旨在教您有关客户满意度流程的所有内容 - 基本的CSAT概念、创建CSAT调查的好处、如何创建CSAT调查。配图来源&#xff1a; SaleSmartly(ss客服) 一、什么是 CSAT&#xff1…...

AD域备份和恢复工具

Microsoft的本地Active Directory备份和恢复功能不适用于对象级备份和属性级还原。使用RecoveryManager Plus&#xff0c;您不仅可以备份和还原所有AD对象&#xff0c;还可以备份和还原其他基本AD元素&#xff0c;例如架构属性&#xff0c;组成员身份信息和Exchange属性。此外&…...

老学长的浙大MPA现场复试经验分享

作为一名在浙大MPA项目已经毕业的考生来说&#xff0c;很荣幸受到杭州达立易考周老师的邀请&#xff0c;给大家分享下我的复试经验&#xff0c;因为听周老师说是这几年浙大MPA因疫情情况&#xff0c;已经连续几年都是线上个人复试了&#xff0c;而今年疫情社会面较为平稳的情况…...

制作证书链并进行验证

生成自签名的根证书: openssl req -x509 -newkey rsa -outform PEM -out tls-rootca.pem -keyform PEM -keyout tls-rootca.key.pem -days 35600 -nodes -subj “/C=cn/O=mycomp/OU=mygroup/CN=rootca” 生成中间证书 1.生成csr和key文件 openssl req -newkey rsa:2048 -outf…...

基于python多光谱遥感数据处理、图像分类、定量评估及机器学习方法应用

基于卫星或无人机平台的多光谱数据在地质、土壤调查和农业等应用领域发挥了重要作用&#xff0c;在地质应用方面&#xff0c;综合Aster的短波红外波段、landsat热红外波段等多光谱数据&#xff0c;可以通过不同的多光谱数据组合&#xff0c;协同用于矿物信息有效提取。此外&…...

初识 git--本地仓库

目录&#xff1a;一&#xff0c;基础步骤&#xff1a;1&#xff0c;安装2&#xff0c;配置3&#xff0c;检查配置4&#xff0c;创建仓库 - repository5&#xff0c;查看工作区的文件状态6&#xff0c;如果显示乱码的解决方式git status 显示乱码终端乱码7&#xff0c;添加工作区…...