[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加载加载微服务的方式有两种:
- 是注册加载,通过劫持路由,会根据路由变化加载每个微服务
registerMicroApps,是根据路由的变化加载微服务的,只要路由变化,就会触发微服务重新加载微服务,因此无法阻止微服务的重新加载,缓存不能被使用,指定容器的内容会被覆盖,因此感觉不适合缓存页签的方案 - 是手动加载,可以自己决定什么时候加载
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)]](https://img-blog.csdnimg.cn/0848993da1094f72aad526d4bf55520c.png)
vue 的组件,不同路径,同一个路由, 缓存的组件为同一个
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7wZSirDR-1677045679177)(2023-01-03-15-16-52.png)]](https://img-blog.csdnimg.cn/e18bd544192a48beb35b411a8ff89465.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转化横向与阵列 相乘,竖向为或阵列 相加!功率放大电路克服交越失真,是在乙类的基础上增加两个二极管,使微导通,使三极管导通时间大于半个周期,小于一个周期,构成甲乙类工作状态。选择填空…...
vue+echarts:圆形柱状图设置角度和最大值
第020个点击查看专栏目录本示例是显示圆形的柱状图,angleAxis设置一个max, angleAxis上startAngle:90 , 将0点设置为最顶点。 文章目录示例效果示例源代码(共100行)相关资料参考专栏介绍示例效果 示例源代码…...
Linux系统安装Nginx常见报错问题
安装Nginx从nginx官网下载所需版本的nginx,http://nginx.org/下载之后,将安装包上传到linux系统指定路径解压文件,tar -zxvf nginx-1.22.1.tar.gz (此处用1.22.1版本为例)进入安装包目录,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 常用技巧 命令调色板 根据您当前的上下文访问所有可用的命令。 键盘快捷键:⇧⌘P 快速打开 快速打开文件。 键盘快捷键:⌘P **提示:**类型?查看命令建议。 在最近打开的文件夹和工作区之间导航 最近打开 键盘快捷…...
MAC地址IP地址 端口
网络结构: 服务器-客户机(C/S)Client-Server结构,如QQ,LOL都拥有客户端 优点:响应速度快,形式多样,安全新较高缺点:安装软件和维护,不能跨平台LINUX/windows/MAC浏览器-…...
关于虚拟数字人你想知道的都在这里
2022年底,微软旗下的人工智能实验室Open AI发布的对话式大型语言模型ChatGPT聊天机器人一夜蹿红,5天用户量超百万,在各大中外媒体平台掀起了一阵热潮。也带火了人工智能相关产业,AI虚拟数字人就是其中之一,一个随着元宇…...
分布式任务调度处理方案(无代码)
业务涉及到,需要向数据库、redis、elasticsearch、MinIO写四份数据,这里存在分布式事务问题。如何解决问题,先分析cap,是要保证可用性,还是保证一致性。如何选择是CP还是AP?分析业务场景CP的场景࿱…...
2023年博管办香江学者计划、澳门青年学者开始申报
2023年2月20日,全国博士后管委会办公室官方网站发出了2023年香江学者计划、澳门青年学者计划和博士后国(境)外学术交流项目申报指南,以下知识人网小编仅转载香江学者计划和澳门青年学者计划申报指南并做重点解读。知识人网整理香江…...
(二十一)、实现评论功能(1)【uniapp+uinicloud多用户社区博客实战项目(完整开发文档-从零到完整项目)】
1,评论回复模块的样式布局 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#入门,就是一笔记,算不上原创,没办法得选啊。 一、vs中卸载项目和移除项目有什么区别? 1、卸载、移除都不会移除物理文件,只会删除关联 2、卸载删除关联的程度低,卸载后项目只是“变灰色…...
【数据结构与算法】算法的时间复杂度和空间复杂度
文章目录前言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的方式,添加计划任务 crond 服务的周期任务的文件存放位置在:/var/spool/cron/ 如果你是root用户的话那么你的周期任务文件名就叫root 如果你使用其他用户创建的周期任务,任务文件名就叫它本身 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等文件外,最为常见的…...
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反射机制: 是指在运行时去获取一个类的变量和方法信息。然后通过获取的信息来创建对象,调用方法的一种机制。由于这种<动态性>,可以极大的增强程序的灵活性,程序不用在编译期就完成确定,在运行期仍…...
构造agent类型的内存马(内存马系列篇十三)
写在前面 前面我们对JAVA中的Agent技术进行了简单的学习,学习前面的Agent技术是为了给这篇Agent内存马的实现做出铺垫,接下来我们就来看看Agent内存马的实现。 这是内存马系列篇的第十三篇了。 环境搭建 我这里就使用Springboot来搭建一个简单的漏洞…...
设计模式和设计原则回顾
设计模式和设计原则回顾 23种设计模式是设计原则的完美体现,设计原则设计原则是设计模式的理论基石, 设计模式 在经典的设计模式分类中(如《设计模式:可复用面向对象软件的基础》一书中),总共有23种设计模式,分为三大类: 一、创建型模式(5种) 1. 单例模式(Sing…...
Linux 文件类型,目录与路径,文件与目录管理
文件类型 后面的字符表示文件类型标志 普通文件:-(纯文本文件,二进制文件,数据格式文件) 如文本文件、图片、程序文件等。 目录文件:d(directory) 用来存放其他文件或子目录。 设备…...
C++初阶-list的底层
目录 1.std::list实现的所有代码 2.list的简单介绍 2.1实现list的类 2.2_list_iterator的实现 2.2.1_list_iterator实现的原因和好处 2.2.2_list_iterator实现 2.3_list_node的实现 2.3.1. 避免递归的模板依赖 2.3.2. 内存布局一致性 2.3.3. 类型安全的替代方案 2.3.…...
基于uniapp+WebSocket实现聊天对话、消息监听、消息推送、聊天室等功能,多端兼容
基于 UniApp + WebSocket实现多端兼容的实时通讯系统,涵盖WebSocket连接建立、消息收发机制、多端兼容性配置、消息实时监听等功能,适配微信小程序、H5、Android、iOS等终端 目录 技术选型分析WebSocket协议优势UniApp跨平台特性WebSocket 基础实现连接管理消息收发连接…...
【SpringBoot】100、SpringBoot中使用自定义注解+AOP实现参数自动解密
在实际项目中,用户注册、登录、修改密码等操作,都涉及到参数传输安全问题。所以我们需要在前端对账户、密码等敏感信息加密传输,在后端接收到数据后能自动解密。 1、引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId...
令牌桶 滑动窗口->限流 分布式信号量->限并发的原理 lua脚本分析介绍
文章目录 前言限流限制并发的实际理解限流令牌桶代码实现结果分析令牌桶lua的模拟实现原理总结: 滑动窗口代码实现结果分析lua脚本原理解析 限并发分布式信号量代码实现结果分析lua脚本实现原理 双注解去实现限流 并发结果分析: 实际业务去理解体会统一注…...
Go 语言并发编程基础:无缓冲与有缓冲通道
在上一章节中,我们了解了 Channel 的基本用法。本章将重点分析 Go 中通道的两种类型 —— 无缓冲通道与有缓冲通道,它们在并发编程中各具特点和应用场景。 一、通道的基本分类 类型定义形式特点无缓冲通道make(chan T)发送和接收都必须准备好࿰…...
使用LangGraph和LangSmith构建多智能体人工智能系统
现在,通过组合几个较小的子智能体来创建一个强大的人工智能智能体正成为一种趋势。但这也带来了一些挑战,比如减少幻觉、管理对话流程、在测试期间留意智能体的工作方式、允许人工介入以及评估其性能。你需要进行大量的反复试验。 在这篇博客〔原作者&a…...
【Kafka】Kafka从入门到实战:构建高吞吐量分布式消息系统
Kafka从入门到实战:构建高吞吐量分布式消息系统 一、Kafka概述 Apache Kafka是一个分布式流处理平台,最初由LinkedIn开发,后成为Apache顶级项目。它被设计用于高吞吐量、低延迟的消息处理,能够处理来自多个生产者的海量数据,并将这些数据实时传递给消费者。 Kafka核心特…...
【51单片机】4. 模块化编程与LCD1602Debug
1. 什么是模块化编程 传统编程会将所有函数放在main.c中,如果使用的模块多,一个文件内会有很多代码,不利于组织和管理 模块化编程则是将各个模块的代码放在不同的.c文件里,在.h文件里提供外部可调用函数声明,其他.c文…...
