项目部署上线后,特别是网页项目,提示正在操作系统的用户去更新版本非常 important。一般我们都会用“刷新大法”来清理缓存,但是对于正在操作网页的用户,不造系统更新了,请求的还是老版本的资源。
为了确保用户能够及时获得最新的功能和修复的 bug,我们需要通知用户刷新页面获取最新的代码。
每次打包时,都生成一个时间戳,作为系统的伪版本,放到JSON文件中,通过对比文件的响应头Etag判断是否有更新。具体步骤如下:
路由守卫router.beforeResolve(Vue-Router为例)
,检查更新,对比manifest.json文件的响应头Etag判断是否有更新通过Worker轮询
,检查更新,对比manifest.json文件的响应头Etag判断是否有更新。Worker线程并不影响其他线程的逻辑。整体逻辑如下所示:
{
"timestamp":21312321311,
"msg":"更新内容如下:\n--1.添加系统更新提示机制"
}
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
}
})
})
新建 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)
}
}
新建 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()
}
})
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初学者和前端开发人员使用,也可以作为网课、培训机构与大中专院校的教学用书。
文章浏览阅读3.8k次,点赞9次,收藏28次。直接上一个工作中碰到的问题,另外一个系统开启多线程调用我这边的接口,然后我这边会开启多线程批量查询第三方接口并且返回给调用方。使用的是两三年前别人遗留下来的方法,放到线上后发现确实是可以正常取到结果,但是一旦调用,CPU占用就直接100%(部署环境是win server服务器)。因此查看了下相关的老代码并使用JProfiler查看发现是在某个while循环的时候有问题。具体项目代码就不贴了,类似于下面这段代码。while(flag) {//your code;}这里的flag._main函数使用while(1)循环cpu占用99
文章浏览阅读347次。idea shift f6 快捷键无效_idea shift +f6快捷键不生效
文章浏览阅读135次。Ecmacript 中没有DOM 和 BOM核心模块Node为JavaScript提供了很多服务器级别,这些API绝大多数都被包装到了一个具名和核心模块中了,例如文件操作的 fs 核心模块 ,http服务构建的http 模块 path 路径操作模块 os 操作系统信息模块// 用来获取机器信息的var os = require('os')// 用来操作路径的var path = require('path')// 获取当前机器的 CPU 信息console.log(os.cpus._node模块中有很多核心模块,以下不属于核心模块,使用时需下载的是
文章浏览阅读10w+次,点赞435次,收藏3.4k次。SPSS 22 下载安装过程7.6 方差分析与回归分析的SPSS实现7.6.1 SPSS软件概述1 SPSS版本与安装2 SPSS界面3 SPSS特点4 SPSS数据7.6.2 SPSS与方差分析1 单因素方差分析2 双因素方差分析7.6.3 SPSS与回归分析SPSS回归分析过程牙膏价格问题的回归分析_化工数学模型数据回归软件
文章浏览阅读7.5k次。如何利用hutool工具包实现邮件发送功能呢?1、首先引入hutool依赖<dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.7.19</version></dependency>2、编写邮件发送工具类package com.pc.c..._hutool发送邮件
文章浏览阅读867次,点赞2次,收藏2次。docker安装elasticsearch,elasticsearch-head,kibana,ik分词器安装方式基本有两种,一种是pull的方式,一种是Dockerfile的方式,由于pull的方式pull下来后还需配置许多东西且不便于复用,个人比较喜欢使用Dockerfile的方式所有docker支持的镜像基本都在https://hub.docker.com/docker的官网上能找到合..._docker安装kibana连接elasticsearch并且elasticsearch有密码
文章浏览阅读1.3w次,点赞57次,收藏92次。整理 | 郑丽媛出品 | CSDN(ID:CSDNnews)近年来,随着机器学习的兴起,有一门编程语言逐渐变得火热——Python。得益于其针对机器学习提供了大量开源框架和第三方模块,内置..._beeware
文章浏览阅读7.9k次。//// ViewController.swift// Day_10_Timer//// Created by dongqiangfei on 2018/10/15.// Copyright 2018年 飞飞. All rights reserved.//import UIKitclass ViewController: UIViewController { ..._swift timer 暂停
文章浏览阅读986次,点赞2次,收藏2次。1.硬性等待让当前线程暂停执行,应用场景:代码执行速度太快了,但是UI元素没有立马加载出来,造成两者不同步,这时候就可以让代码等待一下,再去执行找元素的动作线程休眠,强制等待 Thread.sleep(long mills)package com.example.demo;import org.junit.jupiter.api.Test;import org.openqa.selenium.By;import org.openqa.selenium.firefox.Firefox.._元素三大等待
文章浏览阅读3k次,点赞4次,收藏14次。Java软件工程师职位分析_java岗位分析
文章浏览阅读2k次。Java:Unreachable code的解决方法_java unreachable code
文章浏览阅读1w次。1、html中设置标签data-*的值 标题 11111 222222、点击获取当前标签的data-url的值$('dd').on('click', function() { var urlVal = $(this).data('ur_如何根据data-*属性获取对应的标签对象