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

目录

  • 前言
  • 解决方案
    • 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 前。
在这里插入图片描述

好书推荐

《循序渐进Vue.js 3.x前端开发实战》:京东直达

本书以一个多年前端“老司机”的视角,循序渐进地介绍流行前端框架Vue.js 3.x全家桶与周边工具在商业项目开发中的应用。

本书通俗易懂,范例丰富,原理与实践并重,适合Vue.js初学者和前端开发人员使用,也可以作为网课、培训机构与大中专院校的教学用书。

在这里插入图片描述

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

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

相关文章

【开发环境搭建篇】Redis客户端安装和配置

作者介绍:本人笔名姑苏老陈,从事JAVA开发工作十多年了,带过大学刚毕业的实习生,也带过技术团队。最近有个朋友的表弟,马上要大学毕业了,想从事JAVA开发工作,但不知道从何处入手。于是&#xff0…

UniTask 异步任务

文章目录 前言一、UniTask是什么?二、使用步骤三、常用的UniTask API和示例1.编写异步方法2.处理异常3.延迟执行4.等待多个UniTask或者一个UniTas完成5.异步加载资源示例6.手动控制UniTask的完成状态7.UniTask.Lazy延迟任务的创建8.后台线程切换Unity主线程9.不要返…

力扣爆刷第102天之hot100五连刷96-100

力扣爆刷第102天之hot100五连刷96-100 文章目录 力扣爆刷第102天之hot100五连刷96-100一、136. 只出现一次的数字二、169. 多数元素三、75. 颜色分类四、31. 下一个排列五、287. 寻找重复数 一、136. 只出现一次的数字 题目链接:https://leetcode.cn/problems/sing…

C语言字符函数与字符串函数:编织文字的舞会之梦(下)

欢迎来到白刘的领域 Miracle_86.-CSDN博客 系列专栏 C语言知识 先赞后看,已成习惯 创作不易,多多支持! 目录 七、strncpy的使用以及模拟实现 八、strncat的使用以及模拟实现 九、strncmp的使用以及模拟实现 十、strstr的使用以及模拟…

设计模式之抽象工厂模式解析

抽象工厂模式 1)问题 工厂方法模式中的每个工厂只生产一类产品,会导致系统中存在大量的工厂类,增加系统的开销。 2)概述 a)产品族 和 产品等级结构 产品等级结构:产品的继承结构; 产品族&…

华为ensp中ospf基础 原理及配置命令(详解)

CSDN 成就一亿技术人! 作者主页:点击! ENSP专栏:点击! CSDN 成就一亿技术人! ————前言———— OSPF 的全称是 Open Shortest Path First,意为“开放式最短路径优先”。是一种内部网关协…

银行OA系统|基于SpringBoot架构+ Mysql+Java+ B/S结构的银行OA系统设计与实现(可运行源码+数据库+设计文档)

推荐阅读100套最新项目 最新ssmjava项目文档视频演示可运行源码分享 最新jspjava项目文档视频演示可运行源码分享 最新Spring Boot项目文档视频演示可运行源码分享 2024年56套包含java,ssm,springboot的平台设计与实现项目系统开发资源(可…

Occupancy 训练策略

损失函数 Dice Loss Dice Loss 是一种用于图像分割的损失函数,其灵感来自于Dice 系数,是一种衡量两个样本相似度的方法。Dice 系数定义为: Dice 系数 2 * TP / (2 * TP FP FN) 其中: TP:预测为正且真实值为正的像…

网易有道 3/22

一面还比较常规,二面真的是压力爆炸,还是感觉自己太菜了 一面计网部分直接就是垮了,二面的话面试官水平很高,根本就和我不是一个级别的,三言两语就知道了我的项目大致情况,然后不断拷打 项目问完了又问了…

OpenGL学习笔记【3】—— GLAD配置

一、为什么用GLAD 由于OpenGL驱动版本众多,它大多数函数的位置都无法在编译时确定下来,需要在运行时查询。所以任务就落在了开发者身上,开发者需要在运行时获取函数地址并将其保存在一个函数指针中供以后使用。取得地址的方法因平台而异&…

超声波清洗机是用来干什么的?好用眼镜超声波清洗机不能错过

在快节奏的现代生活中,每一项提高效率和清洁效果的技术都值得我们关注。超声波清洗机就是这样一种技术的完美体现,它通过高频声波在液体中产生微小的气泡,这些气泡在压力作用下迅速爆炸,产生的强大冲击力能够深入物品的微小缝隙&a…

阿里云权益中心2024年五大上云优惠权益解析,助力用户优惠上云

上云首选,普惠好价,2024年阿里云通过权益中心为个人和企业用户提供五大上云优惠权益,为开发者和企业提供多款新老同享、续费同价产品;超150款免费试用产品;初创企业最高可得最低3500元,最高100万上云抵扣金…

Docker 安装 Skywalking以及UI界面

关于Skywalking 在现代分布式系统架构中,应用性能监控(Application Performance Monitoring, APM)扮演着至关重要的角色。本文将聚焦于一款备受瞩目的开源APM工具——Apache Skywalking,通过对其功能特性和工作原理的详细介绍&am…

Unity UGUI之Toggle基本了解

在Unity中,Toggle一般用于两种状态之间的切换,通常用于开关或复选框等功能。 它的基本属性如图: 其中, Interactable(可交互):指示Toggle是否可以与用户交互。设置为false时,禁用To…

Leetcode 70.爬楼梯

心路历程: 这道题是之前学院的一道复试题,大家都没怎么刷过算法题,只记得当年凭借几次试错自己把这道题做出来了,当时也不知道动态规划之类的。 正常来讲,这种找不到循环结构的题一般都是递归解决。 注意的点&#x…

Java语法学习八之认识String类

String类的重要性 在C语言中已经涉及到字符串了,但是在C语言中要表示字符串只能使用字符数组或者字符指针,可以使用标准库提供的字符串系列函数完成大部分操作,但是这种将数据和操作数据方法分离开的方式不符合面相对象的思想,而…

使用 Pytorch 和 Rasterio 的自定义地理空间数据加载器

地理空间数据在从遥感和城市规划到环境监测和灾害管理的各个领域发挥着至关重要的作用。在处理机器学习任务的地理空间数据时,准备自定义数据加载器对于有效加载、预处理和增强数据而不丢失其属性至关重要,特别是当输入图像具有超过 3 个波段时。 Rasterio确实是一个专门为有…

【嵌入式——QT】QT Charts

【嵌入式——QT】QT Charts 概述Qt提供的坐标轴类QChartQLineSeriesQValueAxis常见图表及用到的序列类图示代码示例 概述 QT Charts模块是一组易于使用的图表组件,它基于Qt的Graphics View架构,其核心组件是QChartView和QChart,QChartView父…

软硬件全开源,方案分享 | 旋钮温控器方案

温控器用于控制室内暖通设备,通过暖通设备为室内环境提供冷源和热源,从而调节室内温度环境,为人们提供舒适的生活环境。 旋钮式温控器更为结构简洁、操作简便,降低了用户的使用难度,提供智能化的控制方法,…

基于时空上下文(STC)的运动目标跟踪算法,Matlab实现

博主简介: 专注、专一于Matlab图像处理学习、交流,matlab图像代码代做/项目合作可以联系(QQ:3249726188) 个人主页:Matlab_ImagePro-CSDN博客 原则:代码均由本人编写完成,非中介,提供…