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

news2024/12/23 17:57:50

目录

  • 前言
  • 解决方案
    • 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
  }
  // 将文件内容转换JSON
  const 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 !== lastEtag
    lastEtag = 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 = true
    confirmReload(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 !== lastEtag
    lastEtag = etag
    console.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 = true
    confirmReload(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和data
    let etag = response.headers.get('etag')
    let data = await response.json()
    hasUpdate = lastEtag !== undefined && etag !== lastEtag
  
    if (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 = false
    lastEtag = data.lastEtag
    intervalId = 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和data
    let etag = response.headers.get('etag')
    let data = await response.json()
    hasUpdate = lastEtag !== undefined && etag !== lastEtag
  
    if (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 = false
    lastEtag = data.lastEtag
    intervalId = 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.coloradmin.cn/o/1525340.html

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

相关文章

深度学习 精选笔记(12)卷积神经网络-理论基础1

学习参考: 动手学深度学习2.0Deep-Learning-with-TensorFlow-bookpytorchlightning ①如有冒犯、请联系侵删。 ②已写完的笔记文章会不定时一直修订修改(删、改、增),以达到集多方教程的精华于一文的目的。 ③非常推荐上面(学习参考&#x…

C语言学习笔记day8

一维数组冒泡排序法 1. 作用 将乱序的一维数组按照从小到大的顺序排列 2. 原理示意图 3. 代码 #include <stdio.h> #include <stdlib.h> #include <time.h>int main(void) {int a[5] {0};int len sizeof(a) / sizeof(a[0]);int i 0;int j 0;int tmp …

【每日算法】理论:常见AIGC模型; 刷题:力扣单调栈

上期文章 【每日算法】理论&#xff1a;生成模型基础&#xff1b; 刷题&#xff1a;力扣单调栈 文章目录 上期文章一、上期问题二、理论问题1、stable diffusion模型的网络架构2、T5的网络架构&#xff08;Text-To-Text Transfer Transformer模型&#xff09;3、SDXL模型4、DA…

打靶记录(个人学习笔记)

一、信息收集 1、主机发现 通过nmap对此网段进行扫描&#xff0c;可以确定靶机ip为192.168.189.144 2、端口扫描 确定了靶机ip之后&#xff0c;我们来扫描端口 发现80端口开放&#xff0c;先访问80端口 用插件识别出一些信息 Wappalyzer插件获得信息&#xff1a;Web服务&am…

服务器开机不输入密码自动进系统, 与设置开机启动项

打开运行[win R ] 输入&#xff1a; control Userpasswords2设置开机启动项 运行 输入 shell:startup在这里插入图片描述

Java自定义注解实现参数校验

1、 定义注解内 自定义如下&#xff0c;本例子以校验手机号码为例 需要校验手机号码标上此注解即可。 其中Mobilelidator 类则是实现自定义校验ConstraintValidator类的接口实现类 2、ConstraintValidator 定义接口实现 ConstraintValidator 自定义接口实现如下图

【打工日常】使用Docker部署zyplayer_doc团队协作文档

一、zyplayer-doc介绍 1.zyplayer-doc是一款适合企业和个人使用的WIKI知识库管理工具&#xff0c;提供在线化的知识库管理功能&#xff0c;专为私有化部署而设计&#xff0c;最大程度上保证企业或个人的数据安全&#xff0c;公司小团队的话完全可以局域网部署一个。 2.它也可以…

计算机冯诺依曼体系结构

文章目录 前言一、冯诺依曼体系结构构成二、各硬件单元之间效率 前言 冯诺依曼体系结构是构成计算机的基本单元&#xff0c;是一个计算机架构&#xff0c;对于计算机来说冯诺依曼体系是基于计算机硬件的一套体系结构&#xff0c;现在的计算机都是遵循冯诺依曼体系结构建立设计…

ZooKeeper是如何保证数据一致性的?

目录 一、分布式一致性原理 二、ZooKeeper架构 2.1 ZAB 协议操作顺序性 2.2 领导者选举 成员身份 成员状态 领导者选举 三、总结 在分布式系统里的多台服务器要对数据状态达成一致&#xff0c;其实是一件很有难度和挑战的事情&#xff0c;因为服务器集群环境的软硬件故障随时…

信息学奥赛一本通之MAC端VSCode C++环境配置

前提 安装 Visual Studio CodeVSCode 中安装 C/C扩展确保 Clang 已经安装&#xff08;在终端中输入命令&#xff1a;clang --version 来确认是否安装&#xff09;未安装&#xff0c;在命令行执行xcode-select --install 命令&#xff0c;会自行安装&#xff0c;安装文件有点大…

ENVI实战—地物波谱分类

实验1&#xff1a;浏览内置波谱库 目的&#xff1a;学会使用波谱浏览器&#xff0c;浏览内置波谱库&#xff0c;以进行课程后继实验 过程&#xff1a; ①打开ENVI软件&#xff0c;在窗口选择“显示”并点击&#xff0c;找到“波谱库浏览器”并打开&#xff0c;此时可以浏览ENVI…

天童美语开学季|开启“热辣滚烫”的新学期

新学期伊始&#xff0c;孩子们即将踏入一个充满挑战和机遇的学习环境。在这个关键时刻&#xff0c;学校和家庭需要更加紧密地协调合作&#xff0c;以确保孩子们能够得到充分的支持和帮助&#xff0c;顺利成长。    在假期生活分享中开启新学期第一课      寒假里孩子们…

聚观早报 | 追觅科技亮相AWE2024;三星家电举办发布会

聚观早报每日整理最值得关注的行业重点事件&#xff0c;帮助大家及时了解最新行业动态&#xff0c;每日读报&#xff0c;就读聚观365资讯简报。 整理丨Cutie 3月18日消息 追觅科技亮相AWE2024 三星家电举办发布会 深蓝汽车将采用华为HI模式 字节跳动2023年全球营收 特斯…

光伏便携式EL检测仪是什么?—科技助农

光伏便携式EL监测仪是一种专门用于检测光伏电池组件性能的高效、实用的设备。它利用电致发光&#xff08;Electroluminescence&#xff0c;EL&#xff09;原理&#xff0c;通过检测光伏板在受到光照后产生的电流所激发出的光线&#xff0c;来评估光伏板的性能。这种设备通常具有…

兰宝传感授权世强硬创代理全线产品,提高工业智能传感器市场渗透率

智能传感器作为工业物联、智能制造的关键核心部件&#xff0c;国产化进程下&#xff0c;当前国内市场对智能传感器的替代需求已从中低端产品延伸至高端产品&#xff0c;上游企业的产品也随之迭代更新。 为满足市场对高端智能传感器的多样化需求&#xff0c;世强先进&#xff0…

Armv8状态寄存器

Processor state AArch64没有与ARMv7当前程序状态寄存器直接对应的寄存器(CPSR)。在AArch64中&#xff0c;传统CPSR的组件以字段的形式提供可独立访问。这些统称为处理器状态(PSTATE)。 在AArch64中&#xff0c;通过执行ERET指令从异常中返回&#xff0c;这会导致要拷贝到PSTAT…

《手把手教你》系列技巧篇(四十)-java+ selenium自动化测试-JavaScript的调用执行-下篇(详解教程)

1.简介 在实际工作中&#xff0c;我们需要对处理的元素进行高亮显示&#xff0c;或者有时候为了看清楚做跟踪鼠标点击了哪些元素需要标记出来。今天宏哥就在这里把这种测试场景讲解和分享一下。 2.用法 创建一个执行 JS 的对象&#xff0c;也就是 JavascriptExecutor 对象&…

Flutter生命周期方法小技巧

需求 A界面跳转到B界面,暂停A界面的音乐或者视频B界面返回到A界面&#xff0c;播放A界面的音乐或者视频A界面切换到后台&#xff0c;暂停A界面的音乐或者视频A界面从后台切换到前台,播放A界面的音乐或者视频 需求通过理解修改为&#xff1a; 监听 StatefulWidget 的 onPause…

基于高斯模型的运动目标检测(车辆检测),Matlab实现

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

机器学习 --- 模型评估、选择与验证

Java实训代码、答案&#xff0c;如果能够帮到您&#xff0c;希望可以点个赞&#xff01;&#xff01;&#xff01; 如果有问题可以csdn私聊或评论&#xff01;&#xff01;&#xff01;感谢您的支持 第1关&#xff1a;为什么要有训练集与测试集 1、下面正确的是&#xff1f;&…