前端项目部署后,如何提示用户版本更新

目录

  • 前言
  • 解决方案
    • 1、public目录下新建manifest.json
    • 2、写入当前时间戳到manifest.json
    • 3、检查版本更新
    • 4、woker线程
    • 5、入口文件引入
  • 可能出现的问题

在这里插入图片描述

前言

项目部署上线后,特别是网页项目,提示正在操作系统的用户去更新版本非常 important。一般我们都会用“刷新大法”来清理缓存,但是对于正在操作网页的用户,不造系统更新了,请求的还是老版本的资源。

为了确保用户能够及时获得最新的功能和修复的 bug,我们需要通知用户刷新页面获取最新的代码。

解决方案

每次打包时,都生成一个时间戳,作为系统的伪版本,放到JSON文件中,通过对比文件的响应头Etag判断是否有更新。具体步骤如下:

  1. 在public文件夹下加入manifest.json文件,里面存放两个字段:更新内容、更新时间戳
  2. 前端打包的时候向manifest.json写入当前时间戳信息
  3. 在入口文件main.js中引入检查版本更新的逻辑,有更新则提示更新。有两种方式提示提示用户:
    • 路由守卫router.beforeResolve(Vue-Router为例),检查更新,对比manifest.json文件的响应头Etag判断是否有更新
    • 通过Worker轮询,检查更新,对比manifest.json文件的响应头Etag判断是否有更新。Worker线程并不影响其他线程的逻辑。

整体逻辑如下所示:

在这里插入图片描述

1、public目录下新建manifest.json

{"timestamp":21312321311,"msg":"更新内容如下:\n--1.添加系统更新提示机制"
}

2、写入当前时间戳到manifest.json

vue.config.js文件中


const { readFile, writeFile } = require('fs')
// 获取路径
const filePath = path.resolve(`./public`, 'manifest.json')
// 读取文件内容
readFile(filePath, 'utf8', (err, data) => {if (err) {console.error('读取文件时出错:', err)return}// 将文件内容转换JSONconst dataObj = JSON.parse(data)//修改时间戳dataObj.timestamp = new Date().getTime()// 将修改后的内容写回文件writeFile(filePath, JSON.stringify(dataObj), 'utf8', err => {if (err) {console.error('写入文件时出错:', err)return}})
})

3、检查版本更新

新建 checkUpdate.js 文件

(1)初始化变量

import Worker from "./checkUpdate.worker.js";
import router from '../router'
//上次的Etag
let lastEtag = ''//是否更新
let hasUpdate = false
//创建worker线程
const worker = new Worker();

(2)检查版本更新

//检查版本更新
async function checkUpdate() {try {// 检测前端资源是否有更新let response = await fetch(`/manifest.json?v=${Date.now()}`, {method: 'head'})// 获取最新的etag  let etag = response.headers.get('etag')hasUpdate = lastEtag && etag !== lastEtaglastEtag = etag} catch (e) {return Promise.reject(e)}
}

其中let response = await fetch(/manifest.json?v=${Date.now()}, { method: 'head' })
使用 fetch 函数发起了一个 HTTP 请求,获取了指定资源的头信息(HTTP 头部)。其中 manifest.json 是要请求的资源,Date.now() 会生成当前时间的时间戳,作为查询参数 v 的值,这样可以避免浏览器缓存,强制获取最新的资源。请求方式为 HEAD,这意味着只请求资源的头部信息而不获取具体的内容。

let etag = response.headers.get('etag'),从 HTTP 响应中获取了 ETag 头部信息,ETag 是服务器生成的资源唯一标识,用于检查资源是否发生了变化。具体比较逻辑如下:

1、客户端发起请求,请求中包含上次获取的资源的ETag。
2、服务器收到请求后,比较客户端提供的ETag与当前资源的ETag是否一致。
3、如果一致,则返回HTTP 304 Not Modified响应,表示资源未发生变化,客户端可以使用缓存的版本。
4、如果不一致,服务器返回最新的资源内容,同时更新ETag。
5、客户端收到响应后,更新本地缓存的资源内容和ETag。

(3)路由跳转检测版本更新

 // 路由拦截router.beforeEach(async (to, from, next) => {next()try {await checkUpdate()if (hasUpdate) {worker.postMessage({type: 'destroy'})location.reload()}} catch (e) {}
})

(4)向worker线程发送检查版本更新逻辑

worker.postMessage({type: 'check'
})

(5)接收到 worker 线程数据更新

worker.onmessage = ({ data }) => {console.log(data,'data')if (data.type === 'hasUpdate') {hasUpdate = trueconfirmReload(data.msg, data.lastEtag)}
}

(6)收到版本更新信息,进行弹框提示
收到版本更新信息后,先暂停轮询检查版本更新,点击确定按钮,则发送destory消息,点击取消按钮则发送recheck消息

async function confirmReload(msg = '', lastEtag) {worker &&worker.postMessage({type: 'pause'})try {//弹框提示逻辑} catch (e) { }
}

checkUpdate.js 全部代码实现

import Worker from "./checkUpdate.worker.js";
import router from '../router'
//上次的Etag
let lastEtag = ''//是否更新
let hasUpdate = false
//创建worker线程
const worker = new Worker();//检查版本更新
async function checkUpdate() {try {// 检测前端资源是否有更新let response = await fetch(`/manifest.json?v=${Date.now()}`, {method: 'head'})// 获取最新的etag  let etag = response.headers.get('etag')hasUpdate = lastEtag && etag !== lastEtaglastEtag = etagconsole.log(lastEtag = etag,'lastEtag = etag')} catch (e) {return Promise.reject(e)}
}async function confirmReload(msg = '', lastEtag) {worker &&worker.postMessage({type: 'pause'})try {console.log('版本更新了')} catch (e) { }
}// 路由拦截router.beforeEach(async (to, from, next) => {next()try {await checkUpdate()if (hasUpdate) {worker.postMessage({type: 'destroy'})location.reload()}} catch (e) {}
})worker.postMessage({type: 'check'
})worker.onmessage = ({ data }) => {console.log(data,'data')if (data.type === 'hasUpdate') {hasUpdate = trueconfirmReload(data.msg, data.lastEtag)}
}

4、woker线程

新建 checkUpdate.worker.js

(1)初始化变量

let lastEtag;//上次的Etag
let hasUpdate = false//是否更新
let intervalId = ''

(2)检查版本更新
逻辑更第三步差不多,唯一一点就是检测到更新后,发送hasUpdate消息,给出弹框提示是否需要更新

async function checkUpdate() {try {// 检测前端资源是否有更新let response = await fetch(`/manifest.json?v=${Date.now()}`, {method: 'get'})// 获取最新的etag和datalet etag = response.headers.get('etag')let data = await response.json()hasUpdate = lastEtag !== undefined && etag !== lastEtagif (hasUpdate) {postMessage({type: 'hasUpdate', msg: data.msg,lastEtag: lastEtag,etag: etag})}lastEtag = etag} catch (e) {return Promise.reject(e)}
}

(3)监听主线程发送过来的数据

// 监听主线程发送过来的数据
addEventListener('message', ({ data }) => {console.log(data,'消息')if (data.type === 'check') {  // 每5分钟执行一次// 立即执行一次,获取最新的etag,避免在setInterval等待中系统更新,第一次获取的etag是新的,但是lastEtag还是undefined,不满足条件,错失刷新时机// checkUpdate()intervalId = setInterval(()=>{checkUpdate()//这里3s方便测试},  3 * 1000)}if (data.type === 'recheck') {// 每5分钟执行一次hasUpdate = falselastEtag = data.lastEtagintervalId = setInterval(()=>{checkUpdate()},  3 * 1000)}if (data.type === 'pause') {clearInterval(intervalId)}if (data.type === 'destroy') {clearInterval(intervalId)close()}
})

完整代码逻辑如下:

let lastEtag
let hasUpdate = false
let intervalId = ''
async function checkUpdate() {try {// 检测前端资源是否有更新let response = await fetch(`/manifest.json?v=${Date.now()}`, {method: 'get'})// 获取最新的etag和datalet etag = response.headers.get('etag')let data = await response.json()hasUpdate = lastEtag !== undefined && etag !== lastEtagif (hasUpdate) {postMessage({type: 'hasUpdate', msg: data.msg,lastEtag: lastEtag,etag: etag})}lastEtag = etag} catch (e) {return Promise.reject(e)}
}// 监听主线程发送过来的数据
addEventListener('message', ({ data }) => {console.log(data,'消息')if (data.type === 'check') {  console.log('checkcheckcheck')// 每5分钟执行一次// 立即执行一次,获取最新的etag,避免在setInterval等待中系统更新,第一次获取的etag是新的,但是lastEtag还是undefined,不满足条件,错失刷新时机// checkUpdate()intervalId = setInterval(()=>{checkUpdate()console.log('检查版本更新')},  3 * 1000)}if (data.type === 'recheck') {// 每5分钟执行一次hasUpdate = falselastEtag = data.lastEtagintervalId = setInterval(()=>{checkUpdate()console.log('检查版本更新')},  3 * 1000)console.log('recheckrecheckrecheck')}if (data.type === 'pause') {clearInterval(intervalId)}if (data.type === 'destroy') {clearInterval(intervalId)close()}
})

5、入口文件引入

import "@/utils/checkUpdate.js"

可能出现的问题

1、worker

webpack 5 可以使用 Web Workers

new Worker(new URL('./checkUpdate.worker.js', import.meta.url));

webpack 5 以下,使用 worker-loader,先安装worker-loader

yarn add worker-loader -D

然后在vue.config.js 配置

  chainWebpack(config) {config.module.rule('worker').test(/\.worker\.js$/).use('worker').loader('worker-loader').end()}

如果配置有问题,会一直出现下面这个问题,worker-loader 的配置要放在其他 loader 前。
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

osg执行opengl4.3的shader报错

运行案例:运行这篇博客的opengl4.3的例子,OSG使用GLSL各个版本例子,报如下错误: Warning: detected OpenGL error invalid operation at after pcp->apply(Unfiorm&) in GLObjectsVisitor::apply(osg::StateSet& stat…

C++ 笛卡尔树

目录 一、性质二、构建笛卡尔树三、应用四、源码 一、性质 堆性质: 笛卡尔树是一种满足堆性质的树。每个节点包含两个值:键值(key)和优先级值(priority)。在笛卡尔树中,根节点的优先级值最大&am…

Prompt提示工程上手指南:基础原理及实践(三)-Prompt个性知识库引导

前言 Prompt系列的第二期文章已经将所有的Prompt工程主流策略讲解完毕,共涉及到六种Prompt类别模型以及具体生产内容详解。再结合系列第一篇文章具体对Prompt工程的详细介绍,也就可以达到Prompt工程师的初步入门,现在如果掌握了这些基础技能…

在pharmit里匹配药效团

我把400个无活性的小分子(decoys)提交到pharmit里。 命名为decoyset00~decoyset08,查找时,按这个找。 1、导入药效团配体: 进入药效团筛选界面: 导入代表药效团模型的活性肽构象: 2、选择预先…

MATLAB环境下基于可调Q因子小波变换的滚动轴承故障诊断(MATLAB R2021B)

小波变换是一种时频局域化方法,它的窗口面积固定但形状可以发生改变(时间窗与频率窗均可变化)。小波变换在时间域与频率域都能够表示信号的局部特征,并具有多分辨率分析的特点,是机械故障诊断中常用的方法。小波变换故…

【C++】手撕红黑树

> 作者简介:დ旧言~,目前大二,现在学习Java,c,c,Python等 > 座右铭:松树千年终是朽,槿花一日自为荣。 > 目标:能直接手撕红黑树。 > 毒鸡汤:行到…

计算机设计大赛 题目:基于深度学习卷积神经网络的花卉识别 - 深度学习 机器视觉

文章目录 0 前言1 项目背景2 花卉识别的基本原理3 算法实现3.1 预处理3.2 特征提取和选择3.3 分类器设计和决策3.4 卷积神经网络基本原理 4 算法实现4.1 花卉图像数据4.2 模块组成 5 项目执行结果6 最后 0 前言 🔥 优质竞赛项目系列,今天要分享的是 基…

OLAP与数据仓库和数据湖

OLAP与数据仓库和数据湖 本文阐述了OLAP、数据仓库和数据湖方面的基础知识以及相关论文。同时记录了我如何通过ChatGPT以及类似产品(通义千问、文心一言)来学习知识的。通过这个过程让我对于用AI科技提升学习和工作效率有了实践经验和切身感受。 预热 …

VSCode+python单步调试库代码

VSCodepython单步调试库代码 随着VSCode版本迭代更新,在最新的1.87.x中,使用Python Debugger扩展进行调试时,扩展的justMyCode默认属性为true,不会进入库中的代码。这对debug而言不太方便,因此需要手动设置一下&#…

leetcode 3080

leetcode 3080 题目 例子 思路 创建数组&#xff0c;记录nums 的值 对应的id, 按照大小排序。 代码实现 class Solution { public:vector<long long> unmarkedSumArray(vector<int>& nums, vector<vector<int>>& queries) {vector<long…

hadoop伪分布式环境搭建详解

&#xff08;操作系统是centos7&#xff09; 1.更改主机名&#xff0c;设置与ip 的映射关系 hostname //查看主机名 vim /etc/hostname //将里面的主机名更改为master vim /etc/hosts //将127.0.0.1后面的主机名更改为master&#xff0c;在后面加入一行IP地址与主机名之间的…

Android VINF和兼容性矩阵

周末搞这玩意欲仙欲死&#xff0c;没办法只有看看。VINTF是供应商接口对象&#xff08;VINTF 对象&#xff09;&#xff0c;准确的说&#xff0c;这个是属于兼容性矩阵概念。。。有点想起了以前看过的一个电影&#xff0c;异次元杀阵。。。 1 基础 这个是谷歌官方的图。 本质…

基于JavaWeb+SSM+Vue“鼻护灵”微信小程序系统的设计和实现

基于JavaWebSSMVue“鼻护灵”微信小程序系统的设计和实现 滑到文末获取源码Lun文目录前言主要技术系统设计功能截图 滑到文末获取源码 Lun文目录 摘 要 3 Abstract 1 1 绪 论 1 1.1研究背景 1 工作的效率。 1 1.2 研究意义 1 1.3研究现状 1 1.4本文组织结构 2 2 技术介绍 3 2…

PyTorch深度学习实战(39)——小样本学习

PyTorch深度学习实战&#xff08;39&#xff09;——小样本学习 0. 前言1. 小样本学习简介2. 孪生网络2.1 模型分析2.2 数据集分析2.3 构建孪生网络 3. 原型网络3. 关系网络小结系列链接 0. 前言 小样本学习 (Few-shot Learning) 旨在解决在训练集中只有很少样本的情况下进行分…

【Leetcode每日一题】 递归 - 两两交换链表中的节点(难度⭐)(38)

1. 题目解析 题目链接&#xff1a;24. 两两交换链表中的节点 这个问题的理解其实相当简单&#xff0c;只需看一下示例&#xff0c;基本就能明白其含义了。 2.算法原理 一、理解递归函数的含义 首先&#xff0c;我们需要明确递归函数的任务&#xff1a;给定一个链表&#xf…

C++学习基础版(二)

目录 五、继承与派生 1、继承和派生 2、三种继承方式 &#xff08;1&#xff09;公有继承【public】 &#xff08;2&#xff09;私有继承【private】 &#xff08;3&#xff09;保护继承【protected】 3、派生类的构造函数 带参数的基类构造函数调用 4、派生类的析构函…

第 126 场 LeetCode 双周赛题解

A 求出加密整数的和 模拟 class Solution { public:int sumOfEncryptedInt(vector<int> &nums) {int res 0;for (auto x: nums) {string s to_string(x);char ch *max_element(s.begin(), s.end());for (auto &c: s)c ch;res stoi(s);}return res;} };B 执行…

JavaEE--小Demo

目录 下载包 配置 修改文件 pom.xml application.properties 创建文件 HelloApi.java GreetingController.java Greeting.java DemoApplication.java 运行包 运行命令 mvn package cd target dir java -jar demo-0.0.1-SNAPSHOT.jar 浏览器测试结果 下载包 …

网站巡检:守护网络空间的看门人

在数字时代&#xff0c;互联网如同一座庞大的信息海洋&#xff0c;每天都有数不清的信息在这里生成、流通和消失。正如一所学校需要门卫来保护安全&#xff0c;网络世界同样需要守护者来确保其内容的健康和安全。在这个背景下&#xff0c;爱校对网站巡检服务应运而生&#xff0…

PCL安装(C++)并配置vs

准备工作&#xff1a; 1.PCL下载包(此教程使用PCL1.11.0) 3.visual studio(此教程使用vs2019) PCL下载&#xff1a; 1、找到自己适合的PCL版本,我选择的是PCL1.11.0。 1.1 Github下载&#xff1a;Releases PointCloudLibrary/pcl GitHub 1.2 百度网盘&#xff1a;https://pan…