通過版本號控制強制刷新瀏覽器或清空瀏覽器緩存

背景介紹

在我們做 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)]

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/37664.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/37664.shtml
英文地址,請注明出處:http://en.pswp.cn/news/37664.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Android descendantFocusability 屬性

view 焦點問題處理 作用 通過該屬性可以指定viewGroup和其子View到底誰獲取焦點&#xff0c; 直接在viewGroup上使用就行。 屬性值 屬性值含義beforeDescendantsviewgroup會優先其子類控件而獲取到焦點afterDescendantsviewgroup只有當其子類控件不需要獲取焦點時才獲取焦點…

MFC創建和使用OCX控件

文章目錄 MFC建立OCX控件注冊OCX控件與反注冊使用Internet Explorer測試ocx控件OCX控件添加方法OCX控件添加事件Web使用OCX控件MFC使用OCX控件使用OCX控件調用ocx的功能函數對ocx的事件響應OCX控件調試工具tstcon32.exe加載ocx控件使用tstcon32.exe調試ocxMFC建立OCX控件 新建…

【ChatGPT 指令大全】怎么使用ChatGPT來輔助知識學習

目錄 概念解說 簡易教學 深度教學 教學與測驗 解釋一個主題的背后原理 總結 在當今信息時代&#xff0c;互聯網的快速發展為我們獲取知識提供了前所未有的便利。而其中&#xff0c;人工智能技術的應用也為我們的學習和交流帶來了新的可能性。作為一種基于自然語言處理的人…

ORA-01704: string literal too long

這是在做數據遷移的時候&#xff0c;將mysql庫中的數據整理成Oracle腳本&#xff0c;接著在客戶端運行sql腳本插入數據時碰到的問題。明顯就是文本太長了導致的報錯&#xff0c;但是實際上設置的字段是可以支持這么長的文本數據的。 解決方案 直接寫程序導出導入數據&#xff0…

Postgresql 基礎使用語法

1.數據類型 1.數字類型 類型 長度 說明 范圍 與其他db比較 Smallint 2字節 小范圍整數類型 32768到32767 integer 4字節 整數類型 2147483648到2147483647 bigint 8字節 大范圍整數類型 -9233203685477808到9223203685477807 decimal 可變 用戶指定 精度小…

rust學習-tokio::time

示例 use std::time::Duration; use tokio::{task, time::interval};#[tokio::main] async fn main() {let mut interval interval(Duration::from_secs(1));let handle task::spawn(async move {loop {interval.tick().await;println!("tick");}});handle.await.…

【大數據】一些基本概念

一、數據庫、數據倉庫、數據湖 1.什么是數據庫 (Database, DB) 數據庫是指長期儲存在計算機中的有組織的, 可共享的數據集合 就是存儲數據的倉庫 數據庫有三個特點: 永久存儲, 有組織, 可共享 數據庫是一種結構化數據存儲技術&#xff0c;用于存儲和管理有組織的數據。數據庫…

微信小程序云開發快速入門(2/4)

前言 我們對《微信小程序云開發快速入門&#xff08;1/4&#xff09;》的知識進行回顧一下。在上章節我們知道了云開發的優勢以及能力&#xff0c;并且我們還完成了碼仔備忘錄的本地版到網絡版的改造&#xff0c;主要學習了云數據庫同時還通過在小程序使用云API直接操作了云數…

SciencePub學術| 智能計量類重點SCIE征稿中

SciencePub學術 刊源推薦: 智能計量類重點SCIE征稿中&#xff01;信息如下&#xff0c;錄滿為止&#xff1a; 一、期刊概況&#xff1a; 智能計量類重點SCIE 【期刊簡介】IF&#xff1a;2.0-2.5&#xff0c;JCR3區&#xff0c;中科院4區&#xff1b; 【版面類型】正刊&#…

new BigDecimal(double val)注意事項 / JWT解析BigDecimal類型數據

前言&#xff1a; 公司項目中有一個板塊需要解析JWT令牌獲取載荷里面封裝的數據&#xff0c;遇到要解析一個BigDecimal類型的數據 問題發現過程&#xff1a; 正常來說&#xff0c;我們解析一個JWT令牌的步驟如下&#xff1a; public static Claims getDataFromToken(String tok…

極狐GitLab 企業級 CI/CD 規模化落地實踐指南(一)

目錄 template 引用&#xff0c;減少代碼冗余&#xff0c;增強 CI/CD 構建擴展性 問題 1&#xff1a;代碼冗余&#xff0c;低效實踐 問題 2&#xff1a;維護性難&#xff0c;工作量大 ? local ? file ? remote ? template 收益 1&#xff1a;一處修改&#xff0c;多…

TIOBE2023年8月榜單發布,Python超越老將C/C++蟬聯冠軍

TIOBE 編程社區指數是一個衡量編程語言受歡迎程度的指標&#xff0c;評判的依據來自世界范圍內的工程師、課程、供應商及搜索引擎&#xff0c;TIOBE 官網近日公布了 2023 年 8 月的編程語言排行榜。 此次的榜單中&#xff0c;Python依舊穩居第一&#xff0c;占比達到了13.33%。…

jpg圖片太大怎么壓縮?這樣做輕松壓縮圖片

圖片太大會給存儲、分享帶來麻煩&#xff0c;但其實現在壓縮圖片大小也不是什么難事&#xff0c;下面就給大家分享幾個一直用的圖片壓縮方法&#xff0c;包含批量壓縮、在線壓縮、免費壓縮等多種方式&#xff0c;大家按需自取哈~ 方法一&#xff1a;嗨格式壓縮大師 這是一個可…

Kotlin Executors線程池newSingleThreadExecutor單線程

Kotlin Executors線程池newSingleThreadExecutor單線程 import java.util.concurrent.Executorsfun main() {val mExecutorService Executors.newSingleThreadExecutor()for (i in 1..5) {mExecutorService.execute {println("seq-$i tid:${Thread.currentThread().threa…

typeScript 之 基礎

工具: PlayGround 源碼&#xff1a; GitHub TypeScript 變量聲明 typeScript中變量聲明注意&#xff1a; 開頭不能以數字開頭變量名稱可以包含數字和字母除了下劃線_和美元$符號外&#xff0c;不能包含其他任意特殊字符 聲明的結構&#xff1a; let 變量名&#xff1a; 類型…

面試經典150題——羅馬數字轉整數

羅馬數字包含以下七種字符: I&#xff0c; V&#xff0c; X&#xff0c; L&#xff0c;C&#xff0c;D 和 M。 字符 數值 I 1 V 5 X 10 L 50 C 100 D 500 M 1000 例如&#x…

docker 學習-- 01 基礎知識

docker 學習-- 01 基礎知識 文章目錄 docker 學習-- 01 基礎知識1.前言1.1 docker 是什么1.2 docker優點1.2.1 統一開發和生產環境:1.2.2 高性能:1.2.3 更輕松的維護和拓展&#xff1a;1.2.4 更輕松的遷移&#xff1a; 1.3 docker缺點1.3.1 運行環境受限1.3.2 文件管理和網絡端…

item_sku-獲取sku詳細信息

一、接口參數說明&#xff1a; item_sku-獲取sku詳細信息&#xff0c;點擊更多API調試&#xff0c;請移步注冊API賬號點擊獲取測試key和secret 公共參數 請求地址: https://api-gw.onebound.cn/taobao/item_sku 名稱類型必須描述keyString是調用key&#xff08;點擊獲取測試…

安全中間件的設計思路和簡單實踐

rasp 的侵入式特性和攔截特性導致開發和運維普通不太愿意配合&#xff0c;當生產環境出現問題時往往第一時間先把責任推給 rasp&#xff0c;逐漸的安全部門普遍只能把 rasp 設置為告警模式&#xff0c;而且越是大的集群攔截開的就越少&#xff0c;所以字節的 elkeid 和某外賣大…

P13-CNN學習1.3-ResNet(神之一手~)

論文地址:CVPR 2016 Open Access Repository https://arxiv.org/pdf/1512.03385.pdf Abstract 翻譯 深層的神經網絡越來越難以訓練。我們提供了一個殘差學習框架用來訓練那些非常深的神經網絡。我們重新定義了網絡的學習方式&#xff0c;讓網絡可以直接學習輸入信息與輸出信息…