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


在这里插入图片描述

前言

项目部署上线后,特别是网页项目,提示正在操作系统的用户去更新版本非常 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 前。
在这里插入图片描述

好书推荐

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

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

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

在这里插入图片描述

  • 93
    点赞
  • 86
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 81
    评论
前端开发是创建WEB页面或APP等前端界面呈现给用户的过程,通过HTML、CSS及JavaScript以及衍生出来的各种技术、框架、解决方案,来实现互联网产品的用户界面交互。 前端开发从网页制作演变而来,名称上有很明显的时代特征。在互联网的演化进程中,网页制作是Web1.0时代的产物,早期网站主要内容都是静态,以图片和文字为主,用户使用网站的行为也以浏览为主。随着互联网技术的发展和HTML5、CSS3的应用,现代网页更加美观,交互效果显著,功能更加强大。 前端开发的流程主要包括: 需求分析:明确网页或应用程序的功能、目标用户、界面风格等。 项目规划:制定详细的项目计划,包括制定项目的时间表、任务分配和前端技术选型等。 设计阶段:进行网页或应用程序的设计工作,包括制定整体布局、设计界面的风格、色彩搭配、字体选用等。 前端开发:根据设计稿进行页面切图,将页面的设计转化为代码,然后使用HTML、CSS和JavaScript等技术语言进行页面的搭建和开发,同时关注页面的交互效果、动画效果等。 前端测试:测试页面的兼容性、响应式布局、用户体验等方面。 上线发布:经过测试确认无误后,将网站或应用程序上线发布。 在前端开发中,会涉及到一些重要的前端技术栈,如vue.js、react.js、node.js、前端安全、react-native等。同时,也需要借助一些工具,如代码编辑工具(WebStorm、VS Code)、代码版本控制工具(Git、SVN)、代码包管理工具(npm、Yarn)、前端构建工具(Webpack、Vite)等,来提升开发效率和质量。 总的来说,前端开发是一个不断发展与进步的领域,随着新的样式解决方案和组件库的不断涌现,以及前端框架的不断更新和演进,前端开发的技术栈和工具链也在不断完善和丰富。未来,前端社区将继续充满朝气与活力,为互联网产品的用户界面交互带来更多的创新和突破。
前端线上部署是指将前端代码部署到生产环境,使用户可以访问和使用最新的前端功能和界面。通知用户有新版本可以通过以下几种方式实现: 1. 弹窗通知:在用户访问网站或应用时,弹出一个通知窗口告知用户有新版本可用。这可以通过在前端代码中添加一个弹窗组件,并在后端部署新版本时触发弹窗通知。 2. 横幅通知:在网站或应用的顶部或底部添加一个横幅,显示有新版本可用的提示信息。这可以通过在前端代码中添加一个固定位置的横幅组件,并在后端部署新版本时触发横幅通知。 3. 电子邮件通知:将新版本的信息发送给用户的注册邮箱,通知他们有新版本可用。这可以通过在后端代码中添加一个发送邮件的功能,并在部署新版本时触发邮件通知。 4. 短信通知:将新版本的信息发送给用户的手机号码,通知他们有新版本可用。这可以通过在后端代码中添加一个发送短信的功能,并在部署新版本时触发短信通知。 5. APP推送通知:对于移动应用,可以使用推送通知的方式通知用户有新版本可用。这可以通过在后端代码中添加一个推送通知的功能,并在部署新版本时触发推送通知。 需要注意的是,以上通知方式需要在前端和后端代码中进行相应的开发和配置。具体的实现方式可以根据项目需求和技术栈来选择和调整。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 81
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不叫猫先生

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值