通过版本号控制强制刷新浏览器或清空浏览器缓存
背景介绍
在我们做 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)若强制清空浏览器缓存标志att1为0则只触发刷新浏览器动作
3)若强制清空浏览器缓存标志att1为1则触发清空浏览器缓存(sessionStorage,cookie)动作
4、注意,以上接口结构可根据自己项目架构实际调整,不一定非得跟我的一致,原理就是后端接口返回了两个字段,一个版本号用于控制是否触发校验机制,一个就是是否同步出发清空浏览器缓存标志。 一定要这俩分开控制,因为有的时候只需要刷新浏览器不需要清缓存。
后端 getVersion() 接口
说明
BaseCodeInfo 为我项目中的数据字典,你可根据你项目中的数据字典进行合理替换,不用单独跟我一样创建一张表。
此表主要有三个控制机制:
- 此功能的全局开关:validstatus (1-开启,0-关闭)
- value:版本号,若想出发前端刷新或情空缓存,版本号必变
- att1:是否强制刷新缓存标志,在 value 变更的前提下,若att1 = 0,则只刷新浏览器,若att1 = 1,则 清空浏览器缓存(sessionStorage,cookie)
- 因为前端的定时器监听时间间隔在秒级别,所以后端要使用 redis 缓存存储 value 以及 att1,否则频繁查询数据库,性能会受到影响。这里注意两个点:
1)当 redis 查不到时,兼容查询数据库是否存在,若存在也可以返回结果,并刷新 redis 数据
2)当修改数据字典的值时,同步刷新 redis
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WG89w8ut-1692003330297)(/tfl/pictures/202308/tapd_32565483_1692002384_767.png)]](https://img-blog.csdnimg.cn/5dcda2f00bae4dbe84705f1dca818420.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)]](https://img-blog.csdnimg.cn/9c41181ef4234142a30dfdc6168bab5a.png)
清空浏览器缓存效果(会退出登录)
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dM0brdzS-1692003330297)(/tfl/pictures/202308/tapd_32565483_1692002485_100.png)]](https://img-blog.csdnimg.cn/96e83abbabe04b75a33775ae129ee9a8.png)
相关文章:
通过版本号控制强制刷新浏览器或清空浏览器缓存
背景介绍 在我们做 web 项目时,经常会遇到一个问题就是,需要 通知业务人员(系统用户)刷新浏览器或者清空浏览器 cookie 缓存的情况。 而对于用户而言,很多人一方面不懂如何操作,另一方面由于执行力问题&am…...
Redis系列(二):深入解读Redis的两种持久化方式
博客地址:blog.zysicyj.top Redis为什么要引入持久化机制 Redis引入持久化机制是为了解决内存数据库的数据安全性和可靠性问题。虽然内存数据库具有高速读写的优势,但由于数据存储在内存中,一旦服务器停止或崩溃,所有数据将会丢失…...
CNN之图像识别
文章目录 1. 图像识别1.1 模式识别1.2 图像识别的过程1.3 图像识别的应用 2. 深度学习发展2.1 深度学习为何崛起2.2 分类与检测2.3 常见的卷积神经网络 3. VGG3.1 VGG163.2 VGG16的结构: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; use option --help for more information # 注意是大写 V nvcc -V export PATH"/usr/local/cuda/bin:$PATH" expor…...
pdf怎么转换成jpg图片?这几个转换方法了解一下
pdf怎么转换成jpg图片?转换PDF文件为JPG图片格式在现代工作中是非常常见的需求,比如将PDF文件中的图表、表格或者图片转换为JPG格式后使用在PPT演示、网页设计等场景中。 【迅捷PDF转换器】是一款非常实用的工具,可以将PDF文件转换成多种不同…...
六轴机械臂码垛货物堆叠仿真
六轴机械臂码垛货物堆叠仿真 1、建立模型与仿真 clear,clc,close all addpath(genpath(.)) %建立模型参数如下: 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–>推断构造方法(默认是无参构造,或指定的构造方法)–>实例化成普通对象(相当于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() 方法)类的实例实例属性的新写法取值函数(getter)和存值函数(setter)属性表达式[Class 表达式](https://es6.ruanyifeng.c…...
企业数字化转型:无形资产占比测算(2007-2021年)
在本次数据中,参考张永珅老师的做法,利用无形资产占比测算数字化转型程度。 一、数据介绍 数据名称:企业数字化转型:无形资产占比 数据年份:2007-2021年 样本数量:32960条 数据说明:包括数…...
[centos]设置主机名
1、设置 hostnamectl set-hostname 名字 2、查看是否生效 hostnamectl status 3、打开一个新链接就可以了...
华为OD真题--新学习选址--带答案
2023华为OD统一考试(AB卷)题库清单-带答案(持续更新)or2023年华为OD真题机考题库大全-带答案(持续更新) 为了解新学期学生暴涨的问题,小乐村要建立所新学校 考虑到学生上学安全问题,需要所有学生家到学校的…...
Qt自定义对话框
介绍 自定义框主要通过对现有对话框QDialog类的派生,根据需求编写成员函数、重载信号函数、槽函数,进而实现在主QWidget中点击某个按钮后,一个对话框的弹出 流程 简化创建派生类 最后点击完成即可。 自定义ui界面,编写成员函数…...
Python 程序设计入门(018)—— format() 函数的用法详解
Python 程序设计入门(018)—— format() 函数的用法详解 目录 Python 程序设计入门(018)—— format() 函数的用法详解一、format() 函数的基本格式二、不提供 format_spec 参数三、设置字符串的对齐方式(align&#x…...
演进式架构
演进能力是一种元特征和保护其他所有架构特征的架构封装器IEEE 的软件架构定义中的41 视图模型。它关注不同角色的不同视角,将整个系统划分成了逻辑视图、开发视图、进程视图和物理视图架构师确定了可审计性、数据、安全性、性能、合法性和伸缩性是该应用的关键架构…...
OCP China Day 2023:五大社区齐聚,加速开源开放创新与落地
8月10日,2023年开放计算中国社区技术峰会(OCP China Day 2023)在北京举行。智慧时代,计算多元化、应用多样化、技术复杂化正驱动数据中心新一轮变革,开源开放社区已成为推动数据中心持续创新的重要力量,通过…...
【Linux】进程间通信之管道
【Linux】进程间通信之管道 进程间通信进程间通信目的进程间通信的方式 管道(内核维护的缓冲区)匿名管道(用于父子间进程间通信)简单使用阻塞状态读写特征非阻塞状态读写特征 匿名管道特点命名管道 匿名管道与命名管道的区别 进程…...
记录一个正则表达式
正则表达式要求如下: 至少 8 个字符; 至少包含一个小写英文字母; 至少包含一个大写英文字母; 至少包含一个数字; 至少包含一个特殊字符,特殊字符为:“!#$%^&*()-” 中的一个; 不包含2个连续…...
IDEA运行Tomcat出现乱码问题解决汇总
最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…...
Python爬虫实战:研究feedparser库相关技术
1. 引言 1.1 研究背景与意义 在当今信息爆炸的时代,互联网上存在着海量的信息资源。RSS(Really Simple Syndication)作为一种标准化的信息聚合技术,被广泛用于网站内容的发布和订阅。通过 RSS,用户可以方便地获取网站更新的内容,而无需频繁访问各个网站。 然而,互联网…...
ardupilot 开发环境eclipse 中import 缺少C++
目录 文章目录 目录摘要1.修复过程摘要 本节主要解决ardupilot 开发环境eclipse 中import 缺少C++,无法导入ardupilot代码,会引起查看不方便的问题。如下图所示 1.修复过程 0.安装ubuntu 软件中自带的eclipse 1.打开eclipse—Help—install new software 2.在 Work with中…...
Java面试专项一-准备篇
一、企业简历筛选规则 一般企业的简历筛选流程:首先由HR先筛选一部分简历后,在将简历给到对应的项目负责人后再进行下一步的操作。 HR如何筛选简历 例如:Boss直聘(招聘方平台) 直接按照条件进行筛选 例如:…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
Web 架构之 CDN 加速原理与落地实践
文章目录 一、思维导图二、正文内容(一)CDN 基础概念1. 定义2. 组成部分 (二)CDN 加速原理1. 请求路由2. 内容缓存3. 内容更新 (三)CDN 落地实践1. 选择 CDN 服务商2. 配置 CDN3. 集成到 Web 架构 …...
用机器学习破解新能源领域的“弃风”难题
音乐发烧友深有体会,玩音乐的本质就是玩电网。火电声音偏暖,水电偏冷,风电偏空旷。至于太阳能发的电,则略显朦胧和单薄。 不知你是否有感觉,近两年家里的音响声音越来越冷,听起来越来越单薄? —…...
论文笔记——相干体技术在裂缝预测中的应用研究
目录 相关地震知识补充地震数据的认识地震几何属性 相干体算法定义基本原理第一代相干体技术:基于互相关的相干体技术(Correlation)第二代相干体技术:基于相似的相干体技术(Semblance)基于多道相似的相干体…...
Yolov8 目标检测蒸馏学习记录
yolov8系列模型蒸馏基本流程,代码下载:这里本人提交了一个demo:djdll/Yolov8_Distillation: Yolov8轻量化_蒸馏代码实现 在轻量化模型设计中,**知识蒸馏(Knowledge Distillation)**被广泛应用,作为提升模型…...
【SSH疑难排查】轻松解决新版OpenSSH连接旧服务器的“no matching...“系列算法协商失败问题
【SSH疑难排查】轻松解决新版OpenSSH连接旧服务器的"no matching..."系列算法协商失败问题 摘要: 近期,在使用较新版本的OpenSSH客户端连接老旧SSH服务器时,会遇到 "no matching key exchange method found", "n…...
