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

背景介绍

在我们做 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(); // 清除所有的缓存数据
                // 第二步:清除所有的cookie
                const 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)]

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/73970.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【C语言】const修饰普通变量和指针

大家好&#xff0c;我是苏貝&#xff0c;本篇博客是系列博客每日一题的第一篇&#xff0c;本系列的题都不会太难&#xff0c;如果大家对这种系列的博客感兴趣的话&#xff0c;可以给我一个赞&#x1f44d;吗&#xff0c;感谢❤️ 文章目录 一.const修饰普通变量二.const修饰指…

Docker容器与虚拟化技术:Docker架构、镜像管理

目录 一、理论 1.Doker概述 2.Docker核心概念 3.Docker安装 4.Docker的镜像管理命令 二、实验 1.Docker安装 2.查看Docker信息 3.Docker的镜像管理命令 三、问题 1.如何注册Docker账号 2.如何设置Docker公共存储库 四、总结 一、理论 1.Doker概述 (1) IT架构 裸…

助力青少年科技创新人才培养,猿辅导投资1亿元设立新基金

近日&#xff0c;在日本千叶县举办的2023年第64届国际数学奥林匹克&#xff08;IMO&#xff09;竞赛公布比赛结果&#xff0c;中国队连续5年获得团体第一。奖牌榜显示&#xff0c;代表中国参赛的6名队员全部获得金牌。其中&#xff0c;猿辅导学员王淳稷、孙启傲分别以42分、39分…

LLaMA长度外推高性价比trick:线性插值法及相关改进源码阅读及相关记录

前言 最近&#xff0c;开源了可商用的llama2&#xff0c;支持长度相比llama1的1024&#xff0c;拓展到了4096长度&#xff0c;然而&#xff0c;相比GPT-4、Claude-2等支持的长度&#xff0c;llama的长度外推显得尤为重要&#xff0c;本文记录了三种网络开源的RoPE改进方式及相…

ArcGIS Pro 基础安装与配置介绍

ArcGIS Pro ArcGIS Pro作为ESRI面向新时代的GIS产品&#xff0c;它在原有的ArcGIS平台上继承了传统桌面软件&#xff08;ArcMap&#xff09;的强大的数据管理、制图、空间分析等能力&#xff0c;还具有其独有的特色功能&#xff0c;例如二三维融合、大数据、矢量切片制作及发布…

用vim打开后中文乱码怎么办

Vim中打开文件乱码主要是文件编码问题。用户可以参考如下解决方法。 1、用vim打开.vimrc配置文件 vim ~/.vimrc**注意&#xff1a;**如果用户根目录下没有.vimrc文件就把/etc/vim/vimrc文件复制过来直接用 cp /etc/vim/vimrc ~/.vimrc2、在.vimrc中加入如下内容 set termen…

树莓派3B CSI摄像头配置

1.硬件连接 1、找到 CSI 接口(树莓派3B的CSI接口在HDMI接口和音频口中间)&#xff0c;需要拉起 CSI 接口挡板,如下&#xff1a; 2、将摄像头排线插入CSI接口。记住&#xff0c;有蓝色胶带的一面应该面向音频口或者网卡方向&#xff0c; 确认方向并插紧排线&#xff0c;将挡板…

app专项测试:app弱网测试

目录 弱网测试背景 网络测试要点 弱网测试关注指标 弱网测试工具 fiddler模拟网络延时场景 网络设置参考 Network Emulator Toolkit模拟网络丢包场景&#xff08;windows网络&#xff09; APP弱网测试 弱网使用工具&#xff1a; app弱网测试要点 APP网络测试要点 网络…

Mysql 搭建MHA高可用架构,实现自动failover,完成主从切换

目录 自动failover MHA&#xff1a; MHA 服务 项目&#xff1a;搭建Mysql主从复制、MHA高可用架构 实验项目IP地址配置&#xff1a; MHA下载地址 项目步骤&#xff1a; 一、修改主机名 二、编写一键安装mha node脚本和一键安装mha mangaer脚本&#xff0c;并执行安装…

网络安全 Day31-运维安全项目-容器架构下

容器架构下 6. Dockerfile6.1 Docker自动化DIY镜像之Dockerfile1) 环境准备2) 书写Dockerfile内容3&#xff09; 运行Dockerfile生成镜像4) 运行容器5) 小结 6.2 案例14&#xff1a;Dockerfile-RUN指令1) 书写Dockerfile2) 构建镜像3) 启动容器4) 测试结果 6.3 Dockerfile指令 …

小程序制作教程:从零开始搭建企业小程序

在如今的数字化时代&#xff0c;企业介绍小程序成为了企业展示与推广的重要工具。通过企业介绍小程序&#xff0c;企业可以向用户展示自己的品牌形象、产品服务以及企业文化等内容&#xff0c;进而提高用户对企业的认知度和信任度。本文将介绍如何从零开始搭建一个企业介绍小程…

【Vue-Router】命名视图

命名视图 同时 (同级) 展示多个视图&#xff0c;而不是嵌套展示&#xff0c;例如创建一个布局&#xff0c;有 sidebar (侧导航) 和 main (主内容) 两个视图&#xff0c;这个时候命名视图就派上用场了。 可以在界面中拥有多个单独命名的视图&#xff0c;而不是只有一个单独的出…

LeetCode150道面试经典题--最后一个单词的长度(简单)

1.题目 给你一个字符串 s&#xff0c;由若干单词组成&#xff0c;单词前后用一些空格字符隔开。返回字符串中 最后一个 单词的长度。 单词 是指仅由字母组成、不包含任何空格字符的最大子字符串。 2.示例 3.思路 通过对字符串的反转&#xff0c;转为数组开始遍历&#xff0c…

Python中使用隧道爬虫ip提升数据爬取效率

作为专业爬虫程序员&#xff0c;我们经常面临需要爬取大量数据的任务。然而&#xff0c;有些网站可能会对频繁的请求进行限制&#xff0c;这就需要我们使用隧道爬虫ip来绕过这些限制&#xff0c;提高数据爬取效率。本文将分享如何在Python中使用隧道爬虫ip实现API请求与响应的技…

UML之四种事物

目录 结构事物 行为事物 分组事物&#xff1a; 注释事物 结构事物 1.类(Class) -类是对一组具有相同属性、方法、关系和语义的对象的描述。一个类实现一个或多个接口 2.接口(interface) -接口描述 了一个类或构件的一个服务的操作集。接口仅仅是定义了一组操作的规范&…

每日后端面试5题 第三天

1. 线程有哪几种状态以及各种状态之间的转换&#xff1f;(必会) 看图&#xff1a; 图片来自 线程状态转换图及其5种状态切换_小曹的blog的博客-CSDN博客 图片来自 总算把线程六种状态的转换说清楚了&#xff01; - 知乎 线程一共有4种状态&#xff0c;分别是&#xff1a; 1.…

【日常积累】RPM包依赖下载及私有yum仓库搭建

概述 某些时候&#xff0c;我们需要下载某个RPM包依赖的依赖。如某些内网环境&#xff0c;就需要自行准备rpm包。可以通过能上互联网的服务器进行相应的rpm包下载&#xff0c;然后在拷贝到相应的服务器安装&#xff0c;或者搭建自己的内容rpm包仓库。 查看*.rpm 包依赖&#…

分布式系统监控zabbix安装部署以及使用

文章目录 分布式系统监控zabbix安装部署及使用一.zabbix监控1.什么是zabbix2.zabbix功能3.zabbix的构成4.zabbix的3种架构4.1 C/S架构4.2 分布式架构&#xff1a;zabbix-proxy-client架构4.3 master-node-client架构 5.zabbix工作原理及数据流向6.zabbix监控模式 二.zabbix部署…

41、可靠传输——停等ARQ

前面两节内容我们学习了传输层的基本概况的一些知识&#xff0c;包括传输层在TCP/IP协议栈中负责的任务、传输层的两大协议&#xff0c;以及端口号、套接字等一些基本的概念。从这一节开始&#xff0c;我们将开启两大协议中TCP协议的学习。 但是&#xff0c;经过之前的学习&am…

Kotlin语法

整理关键语法列表如下&#xff1a; https://developer.android.com/kotlin/interop?hlzh-cn官方指导链接 语法形式 说明 println("count ${countnum}")字符串里取值运算 val count 2 var sum 0 类型自动推导 val 定义只读变量&#xff0c;优先 var定义可变变量…