前端項(xiàng)目部署后如何提示用戶版本更新詳解
前言
項(xiàng)目部署上線后,特別是網(wǎng)頁(yè)項(xiàng)目,提示正在操作系統(tǒng)的用戶去更新版本非常 important。一般我們都會(huì)用“刷新大法”來清理緩存,但是對(duì)于正在操作網(wǎng)頁(yè)的用戶,不造系統(tǒng)更新了,請(qǐng)求的還是老版本的資源。
為了確保用戶能夠及時(shí)獲得最新的功能和修復(fù)的 bug,我們需要通知用戶刷新頁(yè)面獲取最新的代碼。
解決方案
每次打包時(shí),都生成一個(gè)時(shí)間戳,作為系統(tǒng)的偽版本,放到JSON文件中,通過對(duì)比文件的響應(yīng)頭Etag判斷是否有更新。具體步驟如下:
- 在public文件夾下加入manifest.json文件,里面存放兩個(gè)字段:更新內(nèi)容、更新時(shí)間戳
- 前端打包的時(shí)候向manifest.json寫入當(dāng)前時(shí)間戳信息
- 在入口文件main.js中引入檢查版本更新的邏輯,有更新則提示更新。有兩種方式提示提示用戶:
路由守衛(wèi)router.beforeResolve(Vue-Router為例),檢查更新,對(duì)比manifest.json文件的響應(yīng)頭Etag判斷是否有更新- 通過Worker輪詢,檢查更新,對(duì)比manifest.json文件的響應(yīng)頭Etag判斷是否有更新。Worker線程并不影響其他線程的邏輯。
整體邏輯如下所示:

1、public目錄下新建manifest.json
{
"timestamp":21312321311,
"msg":"更新內(nèi)容如下:\n--1.添加系統(tǒng)更新提示機(jī)制"
}
2、寫入當(dāng)前時(shí)間戳到manifest.json
vue.config.js文件中
const { readFile, writeFile } = require('fs')
// 獲取路徑
const filePath = path.resolve(`./public`, 'manifest.json')
// 讀取文件內(nèi)容
readFile(filePath, 'utf8', (err, data) => {
if (err) {
console.error('讀取文件時(shí)出錯(cuò):', err)
return
}
// 將文件內(nèi)容轉(zhuǎn)換JSON
const dataObj = JSON.parse(data)
//修改時(shí)間戳
dataObj.timestamp = new Date().getTime()
// 將修改后的內(nèi)容寫回文件
writeFile(filePath, JSON.stringify(dataObj), 'utf8', err => {
if (err) {
console.error('寫入文件時(shí)出錯(cuò):', err)
return
}
})
})
3、檢查版本更新
新建 checkUpdate.js 文件
(1)初始化變量
import Worker from "./checkUpdate.worker.js"; import router from '../router' //上次的Etag let lastEtag = ''; //是否更新 let hasUpdate = false //創(chuàng)建worker線程 const worker = new Worker();
(2)檢查版本更新
//檢查版本更新
async function checkUpdate() {
try {
// 檢測(cè)前端資源是否有更新
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 函數(shù)發(fā)起了一個(gè) HTTP 請(qǐng)求,獲取了指定資源的頭信息(HTTP 頭部)。其中 manifest.json 是要請(qǐng)求的資源,Date.now() 會(huì)生成當(dāng)前時(shí)間的時(shí)間戳,作為查詢參數(shù) v 的值,這樣可以避免瀏覽器緩存,強(qiáng)制獲取最新的資源。請(qǐng)求方式為 HEAD,這意味著只請(qǐng)求資源的頭部信息而不獲取具體的內(nèi)容。
let etag = response.headers.get('etag'),從 HTTP 響應(yīng)中獲取了 ETag 頭部信息,ETag 是服務(wù)器生成的資源唯一標(biāo)識(shí),用于檢查資源是否發(fā)生了變化。具體比較邏輯如下:
1、客戶端發(fā)起請(qǐng)求,請(qǐng)求中包含上次獲取的資源的ETag。
2、服務(wù)器收到請(qǐng)求后,比較客戶端提供的ETag與當(dāng)前資源的ETag是否一致。
3、如果一致,則返回HTTP 304 Not Modified響應(yīng),表示資源未發(fā)生變化,客戶端可以使用緩存的版本。
4、如果不一致,服務(wù)器返回最新的資源內(nèi)容,同時(shí)更新ETag。
5、客戶端收到響應(yīng)后,更新本地緩存的資源內(nèi)容和ETag。
(3)路由跳轉(zhuǎn)檢測(cè)版本更新
// 路由攔截
router.beforeEach(async (to, from, next) => {
next()
try {
await checkUpdate()
if (hasUpdate) {
worker.postMessage({
type: 'destroy'
})
location.reload()
}
} catch (e) {}
})
(4)向worker線程發(fā)送檢查版本更新邏輯
worker.postMessage({
type: 'check'
})(5)接收到 worker 線程數(shù)據(jù)更新
worker.onmessage = ({ data }) => {
console.log(data,'data')
if (data.type === 'hasUpdate') {
hasUpdate = true
confirmReload(data.msg, data.lastEtag)
}
}
(6)收到版本更新信息,進(jìn)行彈框提示
收到版本更新信息后,先暫停輪詢檢查版本更新,點(diǎn)擊確定按鈕,則發(fā)送destory消息,點(diǎn)擊取消按鈕則發(fā)送recheck消息
async function confirmReload(msg = '', lastEtag) {
worker &&
worker.postMessage({
type: 'pause'
})
try {
//彈框提示邏輯
} catch (e) { }
}
checkUpdate.js 全部代碼實(shí)現(xiàn)
import Worker from "./checkUpdate.worker.js";
import router from '../router'
//上次的Etag
let lastEtag = '';
//是否更新
let hasUpdate = false
//創(chuàng)建worker線程
const worker = new Worker();
//檢查版本更新
async function checkUpdate() {
try {
// 檢測(cè)前端資源是否有更新
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)檢查版本更新
邏輯更第三步差不多,唯一一點(diǎn)就是檢測(cè)到更新后,發(fā)送hasUpdate消息,給出彈框提示是否需要更新
async function checkUpdate() {
try {
// 檢測(cè)前端資源是否有更新
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)監(jiān)聽主線程發(fā)送過來的數(shù)據(jù)
// 監(jiān)聽主線程發(fā)送過來的數(shù)據(jù)
addEventListener('message', ({ data }) => {
console.log(data,'消息')
if (data.type === 'check') {
// 每5分鐘執(zhí)行一次
// 立即執(zhí)行一次,獲取最新的etag,避免在setInterval等待中系統(tǒng)更新,第一次獲取的etag是新的,但是lastEtag還是undefined,不滿足條件,錯(cuò)失刷新時(shí)機(jī)
// checkUpdate()
intervalId = setInterval(()=>{
checkUpdate()
//這里3s方便測(cè)試
}, 3 * 1000)
}
if (data.type === 'recheck') {
// 每5分鐘執(zhí)行一次
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 {
// 檢測(cè)前端資源是否有更新
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)
}
}
// 監(jiān)聽主線程發(fā)送過來的數(shù)據(jù)
addEventListener('message', ({ data }) => {
console.log(data,'消息')
if (data.type === 'check') {
console.log('checkcheckcheck')
// 每5分鐘執(zhí)行一次
// 立即執(zhí)行一次,獲取最新的etag,避免在setInterval等待中系統(tǒng)更新,第一次獲取的etag是新的,但是lastEtag還是undefined,不滿足條件,錯(cuò)失刷新時(shí)機(jī)
// checkUpdate()
intervalId = setInterval(()=>{
checkUpdate()
console.log('檢查版本更新')
}, 3 * 1000)
}
if (data.type === 'recheck') {
// 每5分鐘執(zhí)行一次
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"
可能出現(xiàn)的問題
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()
}
如果配置有問題,會(huì)一直出現(xiàn)下面這個(gè)問題,worker-loader 的配置要放在其他 loader 前。

總結(jié)
到此這篇關(guān)于前端項(xiàng)目部署后如何提示用戶版本更新的文章就介紹到這了,更多相關(guān)前端提示用戶版本更新內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
JS利用ES6和ES5分別實(shí)現(xiàn)長(zhǎng)整數(shù)和字節(jié)數(shù)組互轉(zhuǎn)
這篇文章主要為大家詳細(xì)介紹了長(zhǎng)整數(shù)與字節(jié)數(shù)組互轉(zhuǎn)的技術(shù)原理,文中提供了ES6(現(xiàn)代瀏覽器/Node.js)與ES5(兼容舊環(huán)境)兩套實(shí)現(xiàn)方案,需要的可以參考下2025-04-04
js中document.getElementById(id)的具體用法
本文主要介紹了js中document.getElementById(id)的具體用法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-04-04
JavaScript 十六進(jìn)制RGB色碼轉(zhuǎn)換器
JavaScript 十六進(jìn)制RGB色碼轉(zhuǎn)換器,大家可以學(xué)習(xí)下思路。2009-08-08
使用json來定義函數(shù),在里面可以定義多個(gè)函數(shù)的實(shí)現(xiàn)方法
下面小編就為大家?guī)硪黄褂胘son來定義函數(shù),在里面可以定義多個(gè)函數(shù)的實(shí)現(xiàn)方法。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2016-10-10
JavaScript canvas復(fù)刻蘋果發(fā)布會(huì)環(huán)形進(jìn)度條
canvas 真是一個(gè)好東西,它給前端插上了想象的翅膀,伴隨著 h5 而來,將 web 代入了新的領(lǐng)域。本文將利用anvas復(fù)刻蘋果發(fā)布會(huì)環(huán)形進(jìn)度條,感興趣的可以嘗試一下2022-07-07
微信小程序云開發(fā)之使用云數(shù)據(jù)庫(kù)
這篇文章主要為大家詳細(xì)介紹了微信小程序云開發(fā)之使用云數(shù)據(jù)庫(kù),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-05-05
js采用concat和sort將N個(gè)數(shù)組拼接起來的方法
這篇文章主要介紹了js采用concat和sort將N個(gè)數(shù)組拼接起來的方法,涉及JavaScript針對(duì)數(shù)組的合并與排序操作相關(guān)技巧,需要的朋友可以參考下2016-01-01
擁有一個(gè)屬于自己的javascript表單驗(yàn)證插件
這篇文章主要幫助大家擁有一個(gè)屬于自己的javascript表單驗(yàn)證插件,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-03-03

