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

通过版本号控制强制刷新浏览器或清空浏览器缓存

背景介绍

在我们做 web 项目时,经常会遇到一个问题就是,需要 通知业务人员(系统用户)刷新浏览器或者清空浏览器 cookie 缓存的情况。 而对于用户而言,很多人一方面不懂如何操作,另一方面由于执行力问题,很少有人进行这样的操作。进而遇到常见的缓存问题而上报运维问题。 耽误用户的同时,也增加了我们的运维工作量。 甚至有时研发人员还经常吐槽(都告诉他刷新了,怎么就不刷!)

今天,咱们通过简单的几行代码,来实现灵活控制,是否强制提醒并提供便利的操作让业务人员自动执行此操作!

实现原理

  • 案例前端代码为 vue 工程代码(其他前端架构原理相同)
  • 前端追加一个定时器,定时 N 秒中获取文件/版本号变化情况,来决定是否弹出强制刷新/清缓存提醒。
  • 实现手段
    • 监听后端打包的前端文件是否发生变化(这个办法不佳,因为控制不够灵活)
      • 优点:一劳永逸,只要重新打包发版,就会触发提醒
      • 缺点:控制不够灵活,没法针对刷新浏览器与清空缓存分开控制; 并且有时候不需要刷新也会被刷新
    • 通过后端提供getVersion 接口,动态返回版本号以及是否清空缓存标识来控制
      • 优点:控制更加灵活
      • 缺点:后端需要同步提供一个接口配合使用,然后每次通过修改数据字典的版本号来实现。

前端代码

监听文件变化法(不推荐)

此法优缺点详见实现原理介绍,根据自己诉求选择方案。 有的项目有这样的诉求,完全可以用此法。

此法还有一个最大的弊端

  • 就是监听生效的前提,必须开启页面并聚焦到当前浏览器上才生效。 如果当前没有聚焦到页面,那么当文件变化后,超过定时器的时间间隔后,并不会生效。

auto-update.js 核心源码

// 发版刷新页面,根据监测上传文件实现刷新
import Data from "xe-utils/date";let lastSrcs;
//获取到js名字
const scriptReg = /\<script.*src=["'](?<src>[^"']+)/gm;//获取最新页面中的script链接
async function extractNewScripts() {// _timestamp避免缓存,获取当前时间戳const html = await fetch('/?_timestamp=' + Data.now()).then((resp) =>resp.text());scriptReg.lastIndex = 0;let result = [];let match;while ((match = scriptReg.exec(html))) {result.push(match.groups.src);}return result;
}//进行js文件命名对比
async function needUpdate() {const newScripts = await extractNewScripts();if (!lastSrcs) {lastSrcs = newScripts;return false;}let result = false;if (lastSrcs.length !== newScripts.length) {result = true;}for (let i = 0; i < lastSrcs.length; i++) {if (lastSrcs[i] !== newScripts[i]) {result = true;break;}}lastSrcs = newScripts;return result;
}
//每五秒进行一次比对
const DURATION = 5000;
//出现的弹窗里的文字
function autoRefresh() {setTimeout(async () => {const willUpdate = await needUpdate();if (willUpdate) {const result = confirm('有新功能发布,请点击确定刷新页面!');if (result) {location.reload(true);}}autoRefresh();},DURATION);
}autoRefresh();
  • 在 main.ts(main.js) 里引入auto-update.js
// 页面构建刷新
import './auto-update/auto-update.js';

getVersion 法(推荐)

前端在 App.vue 中追加如下代码
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HlikCEAI-1692003330296)(/tfl/pictures/202308/tapd_32565483_1692001218_668.png)]

下面附上源码

前端追加定时器

created(){// 定时器,每五秒请求一次接口对比版本号setInterval(()=>{this.checkVersion()}, 5000)
},

前端 checkVersion() 核心源码

checkVersion () {// 在需要判断登录状态的地方,获取sessionStorage存储的dialogInfo并验证const dialogInfo = sessionStorage.getItem('dialogInfo');if (dialogInfo) {// 已登录时的处理逻辑// 可以在这里请求接口等操作axios.get(`/baseCode/getVersion`,{headers: {'Cache-Control': 'no-cache'}}) // 反正就是要请求到json文件的内容, 并且禁止缓存.then(res => {// servie版本号const versionServie = res.data.data.value// 当前浏览器端缓存的版本号const clientVersion = localStorage.getItem('_version_')console.log('当前服务器端servie版本号=', versionServie, ',当前浏览器端缓存的版本号=', clientVersion)// 和最新版本不同,刷新页面if (versionServie !== clientVersion) {if (res.data.data.att1 === '1') {// 先刷浏览器,再清缓存(sessionStorage,cookie)const result = confirm('有新功能发布!\n需点击确定自动为您清除浏览器缓存(cookie,sessionStorage)!\n本操作将退出当前登录!');if (result) {//将最新的版本号存储到本地localStorage.setItem('_version_', versionServie)location.reload(true);// 第一步:清除sessionStorage中的缓存数据sessionStorage.clear(); // 清除所有的缓存数据// 第二步:清除所有的cookieconst cookies = document.cookie.split(';');for (let i = 0; i < cookies.length; i++) {const cookie = cookies[i];const eqPos = cookie.indexOf('=');const name = eqPos > -1 ? cookie.substr(0, eqPos) : cookie;document.cookie = `${name}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;`;}}} else {// 只刷新浏览器不清除缓存const result = confirm('有新功能发布,请点击确定刷新页面!');if (result) {localStorage.setItem('_version_', versionServie)location.reload(true);}}}else {//版本号一致不做处理console.log('版本号一致不做处理')}}).catch(error => {console.log(error)})} else {// 未登录时的处理逻辑,跳转到登录页面this.$router.push({ name: 'Login' })}
},

说明:

1、dialogInfo 用来判断当前登录 session 状态,若已登录则进行后续流程判断
2、/baseCode/getVersion 为后端接口,可根据自己项目情况灵活调整
3、业务逻辑
1)若当前浏览器端缓存的版本号 clientVersion 与服务器端返回的版本号versionServie不一致,则触发提醒机制
2)若强制清空浏览器缓存标志att10 则只触发刷新浏览器动作
3)若强制清空浏览器缓存标志att11 则触发清空浏览器缓存(sessionStorage,cookie)动作
4、注意,以上接口结构可根据自己项目架构实际调整,不一定非得跟我的一致,原理就是后端接口返回了两个字段,一个版本号用于控制是否触发校验机制,一个就是是否同步出发清空浏览器缓存标志。 一定要这俩分开控制,因为有的时候只需要刷新浏览器不需要清缓存。

后端 getVersion() 接口

说明

BaseCodeInfo 为我项目中的数据字典,你可根据你项目中的数据字典进行合理替换,不用单独跟我一样创建一张表。
此表主要有三个控制机制:

  1. 此功能的全局开关:validstatus (1-开启,0-关闭)
  2. value:版本号,若想出发前端刷新或情空缓存,版本号必变
  3. att1:是否强制刷新缓存标志,在 value 变更的前提下,若att1 = 0,则只刷新浏览器,若att1 = 1,则 清空浏览器缓存(sessionStorage,cookie)
  4. 因为前端的定时器监听时间间隔在秒级别,所以后端要使用 redis 缓存存储 value 以及 att1,否则频繁查询数据库,性能会受到影响。这里注意两个点:
    1)当 redis 查不到时,兼容查询数据库是否存在,若存在也可以返回结果,并刷新 redis 数据
    2)当修改数据字典的值时,同步刷新 redis
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WG89w8ut-1692003330297)(/tfl/pictures/202308/tapd_32565483_1692002384_767.png)]

getVersion() 接口

/*** value: 版本号* att1:是否同步刷新缓存(1-是,0 否);* <p>* 1、浏览器缓存版本号与 value 不一致则刷新浏览器;* 2、若value不一致的同时 att1等于 1 则同步清缓存** @return*/
public BaseCodeResVO getVersion() {String value = redisTemplate.opsForValue().get(UPGRADE_VERSION);BaseCodeResVO baseCodeResVO = null;if (StringUtils.isNotBlank(value) && value.contains("_")) {String[] arr = value.split("_");if (arr.length > 0) {baseCodeResVO = new BaseCodeResVO();baseCodeResVO.setCode(UPGRADE_VERSION);baseCodeResVO.setValue(arr[0]);baseCodeResVO.setAtt1(arr[1]);return baseCodeResVO;}}BaseCodeInfo baseCodeInfo = baseCodeInfoDao.selectOne(new QueryWrapper<BaseCodeInfo>().lambda().eq(BaseCodeInfo::getType, UPGRADE_VERSION).eq(BaseCodeInfo::getValidStatus, ValidStatusEnum.VALID.value()));if (baseCodeInfo != null) {log.info("从 redis 获取系统升级版本号失败,请及时跟进!");baseCodeResVO = new BaseCodeResVO();baseCodeResVO.setCode(UPGRADE_VERSION);baseCodeResVO.setValue(baseCodeInfo.getValue());baseCodeResVO.setAtt1(baseCodeInfo.getAtt1());String finalValue = baseCodeInfo.getValue() + "_" + baseCodeInfo.getAtt1();redisTemplate.opsForValue().set(UPGRADE_VERSION, finalValue);} else {log.error("获取系统升级版本号失败,value={}", value);}return baseCodeResVO;
}

效果示例

只刷浏览器效果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-z3mYKk5h-1692003330297)(/tfl/pictures/202308/tapd_32565483_1692002508_322.png)]

清空浏览器缓存效果(会退出登录)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dM0brdzS-1692003330297)(/tfl/pictures/202308/tapd_32565483_1692002485_100.png)]

相关文章:

通过版本号控制强制刷新浏览器或清空浏览器缓存

背景介绍 在我们做 web 项目时&#xff0c;经常会遇到一个问题就是&#xff0c;需要 通知业务人员&#xff08;系统用户&#xff09;刷新浏览器或者清空浏览器 cookie 缓存的情况。 而对于用户而言&#xff0c;很多人一方面不懂如何操作&#xff0c;另一方面由于执行力问题&am…...

Redis系列(二):深入解读Redis的两种持久化方式

博客地址&#xff1a;blog.zysicyj.top Redis为什么要引入持久化机制 Redis引入持久化机制是为了解决内存数据库的数据安全性和可靠性问题。虽然内存数据库具有高速读写的优势&#xff0c;但由于数据存储在内存中&#xff0c;一旦服务器停止或崩溃&#xff0c;所有数据将会丢失…...

CNN之图像识别

文章目录 1. 图像识别1.1 模式识别1.2 图像识别的过程1.3 图像识别的应用 2. 深度学习发展2.1 深度学习为何崛起2.2 分类与检测2.3 常见的卷积神经网络 3. VGG3.1 VGG163.2 VGG16的结构&#xff1a;3.3 使用卷积层代替全连接3.4 1*1卷积的作用3.5 VGG16代码示例 4. 残差模型-Re…...

nvcc not found

检查cuda 安装成功 nvidia-smiCommand ‘nvcc’ not found, apt install nvidia-cuda-toolkitnvcc fatal : No input files specified&#xff1b; use option --help for more information # 注意是大写 V nvcc -V export PATH"/usr/local/cuda/bin:$PATH" expor…...

pdf怎么转换成jpg图片?这几个转换方法了解一下

pdf怎么转换成jpg图片&#xff1f;转换PDF文件为JPG图片格式在现代工作中是非常常见的需求&#xff0c;比如将PDF文件中的图表、表格或者图片转换为JPG格式后使用在PPT演示、网页设计等场景中。 【迅捷PDF转换器】是一款非常实用的工具&#xff0c;可以将PDF文件转换成多种不同…...

六轴机械臂码垛货物堆叠仿真

六轴机械臂码垛货物堆叠仿真 1、建立模型与仿真 clear,clc,close all addpath(genpath(.)) %建立模型参数如下&#xff1a; L(1) Link( d, 0.122, a , 0 , alpha, pi/2,offset,0); L(2) Link( d, 0.019 , a ,0.408 , alpha, 0,offset,pi/2); L(3) Link( d, …...

text-decoration 使用

text-decoration text-decoration 用于设置文本上的装饰性线条的外观。 它是 text-decoration-line、text-decoration-style、text-decoration-color 和text-decoration-thickness的缩写。 text-decoration: underline wavy red;text-decoration-line 设置文本装饰类型 可以…...

linux shell快速入门

linux shell快速入门 0 、前置1、简单使用 0 、前置 一安装linux的虚拟环境 1、简单使用 1、新建/usr/shell目录 2、新建hello.sh 文件 3、编写脚本文件# !/bin/bashecho "hello world"查看是否具备执行权限 新增执行权限 chomd x hello.sh执行hello.sh文件 /b…...

【Spring源码】小白速通解析Spring源码,从0到1,持续更新!

Spring源码 参考资料 https://www.bilibili.com/video/BV1Tz4y1a7FM https://www.bilibili.com/video/BV1iz4y1b75q bean的生命周期 bean–>推断构造方法&#xff08;默认是无参构造&#xff0c;或指定的构造方法&#xff09;–>实例化成普通对象&#xff08;相当于ne…...

Unity 鼠标实现对物体的移动、缩放、旋转

文章目录 1. 代码2. 测试场景 1. 代码 using UnityEngine;public class ObjectManipulation : MonoBehaviour {// 缩放比例限制public float MinScale 0.2f;public float MaxScale 3.0f;// 缩放速率private float scaleRate 1f;// 新尺寸private float newScale;// 射线pri…...

67Class 的基本语法

Class 的基本语法 类的由来[constructor() 方法](https://es6.ruanyifeng.com/#docs/class#constructor() 方法)类的实例实例属性的新写法取值函数&#xff08;getter&#xff09;和存值函数&#xff08;setter&#xff09;属性表达式[Class 表达式](https://es6.ruanyifeng.c…...

企业数字化转型:无形资产占比测算(2007-2021年)

在本次数据中&#xff0c;参考张永珅老师的做法&#xff0c;利用无形资产占比测算数字化转型程度。 一、数据介绍 数据名称&#xff1a;企业数字化转型&#xff1a;无形资产占比 数据年份&#xff1a;2007-2021年 样本数量&#xff1a;32960条 数据说明&#xff1a;包括数…...

[centos]设置主机名

1、设置 hostnamectl set-hostname 名字 2、查看是否生效 hostnamectl status 3、打开一个新链接就可以了...

华为OD真题--新学习选址--带答案

2023华为OD统一考试&#xff08;AB卷&#xff09;题库清单-带答案&#xff08;持续更新&#xff09;or2023年华为OD真题机考题库大全-带答案&#xff08;持续更新&#xff09; 为了解新学期学生暴涨的问题,小乐村要建立所新学校 考虑到学生上学安全问题,需要所有学生家到学校的…...

Qt自定义对话框

介绍 自定义框主要通过对现有对话框QDialog类的派生&#xff0c;根据需求编写成员函数、重载信号函数、槽函数&#xff0c;进而实现在主QWidget中点击某个按钮后&#xff0c;一个对话框的弹出 流程 简化创建派生类 最后点击完成即可。 自定义ui界面&#xff0c;编写成员函数…...

Python 程序设计入门(018)—— format() 函数的用法详解

Python 程序设计入门&#xff08;018&#xff09;—— format() 函数的用法详解 目录 Python 程序设计入门&#xff08;018&#xff09;—— format() 函数的用法详解一、format() 函数的基本格式二、不提供 format_spec 参数三、设置字符串的对齐方式&#xff08;align&#x…...

演进式架构

演进能力是一种元特征和保护其他所有架构特征的架构封装器IEEE 的软件架构定义中的41 视图模型。它关注不同角色的不同视角&#xff0c;将整个系统划分成了逻辑视图、开发视图、进程视图和物理视图架构师确定了可审计性、数据、安全性、性能、合法性和伸缩性是该应用的关键架构…...

OCP China Day 2023:五大社区齐聚,加速开源开放创新与落地

8月10日&#xff0c;2023年开放计算中国社区技术峰会&#xff08;OCP China Day 2023&#xff09;在北京举行。智慧时代&#xff0c;计算多元化、应用多样化、技术复杂化正驱动数据中心新一轮变革&#xff0c;开源开放社区已成为推动数据中心持续创新的重要力量&#xff0c;通过…...

【Linux】进程间通信之管道

【Linux】进程间通信之管道 进程间通信进程间通信目的进程间通信的方式 管道&#xff08;内核维护的缓冲区&#xff09;匿名管道&#xff08;用于父子间进程间通信&#xff09;简单使用阻塞状态读写特征非阻塞状态读写特征 匿名管道特点命名管道 匿名管道与命名管道的区别 进程…...

记录一个正则表达式

正则表达式要求如下: 至少 8 个字符&#xff1b; 至少包含一个小写英文字母&#xff1b; 至少包含一个大写英文字母&#xff1b; 至少包含一个数字&#xff1b; 至少包含一个特殊字符&#xff0c;特殊字符为&#xff1a;“!#$%^&*()-” 中的一个&#xff1b; 不包含2个连续…...

零门槛NAS搭建:WinNAS如何让普通电脑秒变私有云?

一、核心优势&#xff1a;专为Windows用户设计的极简NAS WinNAS由深圳耘想存储科技开发&#xff0c;是一款收费低廉但功能全面的Windows NAS工具&#xff0c;主打“无学习成本部署” 。与其他NAS软件相比&#xff0c;其优势在于&#xff1a; 无需硬件改造&#xff1a;将任意W…...

Linux 文件类型,目录与路径,文件与目录管理

文件类型 后面的字符表示文件类型标志 普通文件&#xff1a;-&#xff08;纯文本文件&#xff0c;二进制文件&#xff0c;数据格式文件&#xff09; 如文本文件、图片、程序文件等。 目录文件&#xff1a;d&#xff08;directory&#xff09; 用来存放其他文件或子目录。 设备…...

K8S认证|CKS题库+答案| 11. AppArmor

目录 11. AppArmor 免费获取并激活 CKA_v1.31_模拟系统 题目 开始操作&#xff1a; 1&#xff09;、切换集群 2&#xff09;、切换节点 3&#xff09;、切换到 apparmor 的目录 4&#xff09;、执行 apparmor 策略模块 5&#xff09;、修改 pod 文件 6&#xff09;、…...

2025年能源电力系统与流体力学国际会议 (EPSFD 2025)

2025年能源电力系统与流体力学国际会议&#xff08;EPSFD 2025&#xff09;将于本年度在美丽的杭州盛大召开。作为全球能源、电力系统以及流体力学领域的顶级盛会&#xff0c;EPSFD 2025旨在为来自世界各地的科学家、工程师和研究人员提供一个展示最新研究成果、分享实践经验及…...

Objective-C常用命名规范总结

【OC】常用命名规范总结 文章目录 【OC】常用命名规范总结1.类名&#xff08;Class Name)2.协议名&#xff08;Protocol Name)3.方法名&#xff08;Method Name)4.属性名&#xff08;Property Name&#xff09;5.局部变量/实例变量&#xff08;Local / Instance Variables&…...

视频字幕质量评估的大规模细粒度基准

大家读完觉得有帮助记得关注和点赞&#xff01;&#xff01;&#xff01; 摘要 视频字幕在文本到视频生成任务中起着至关重要的作用&#xff0c;因为它们的质量直接影响所生成视频的语义连贯性和视觉保真度。尽管大型视觉-语言模型&#xff08;VLMs&#xff09;在字幕生成方面…...

SpringCloudGateway 自定义局部过滤器

场景&#xff1a; 将所有请求转化为同一路径请求&#xff08;方便穿网配置&#xff09;在请求头内标识原来路径&#xff0c;然后在将请求分发给不同服务 AllToOneGatewayFilterFactory import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; impor…...

中医有效性探讨

文章目录 西医是如何发展到以生物化学为药理基础的现代医学&#xff1f;传统医学奠基期&#xff08;远古 - 17 世纪&#xff09;近代医学转型期&#xff08;17 世纪 - 19 世纪末&#xff09;​现代医学成熟期&#xff08;20世纪至今&#xff09; 中医的源远流长和一脉相承远古至…...

AI病理诊断七剑下天山,医疗未来触手可及

一、病理诊断困局&#xff1a;刀尖上的医学艺术 1.1 金标准背后的隐痛 病理诊断被誉为"诊断的诊断"&#xff0c;医生需通过显微镜观察组织切片&#xff0c;在细胞迷宫中捕捉癌变信号。某省病理质控报告显示&#xff0c;基层医院误诊率达12%-15%&#xff0c;专家会诊…...

AirSim/Cosys-AirSim 游戏开发(四)外部固定位置监控相机

这个博客介绍了如何通过 settings.json 文件添加一个无人机外的 固定位置监控相机&#xff0c;因为在使用过程中发现 Airsim 对外部监控相机的描述模糊&#xff0c;而 Cosys-Airsim 在官方文档中没有提供外部监控相机设置&#xff0c;最后在源码示例中找到了&#xff0c;所以感…...