前端項目部署后如何提示用戶版本更新詳解
前言
項目部署上線后,特別是網(wǎng)頁項目,提示正在操作系統(tǒng)的用戶去更新版本非常 important。一般我們都會用“刷新大法”來清理緩存,但是對于正在操作網(wǎng)頁的用戶,不造系統(tǒng)更新了,請求的還是老版本的資源。
為了確保用戶能夠及時獲得最新的功能和修復的 bug,我們需要通知用戶刷新頁面獲取最新的代碼。
解決方案
每次打包時,都生成一個時間戳,作為系統(tǒng)的偽版本,放到JSON文件中,通過對比文件的響應頭Etag判斷是否有更新。具體步驟如下:
- 在public文件夾下加入manifest.json文件,里面存放兩個字段:更新內(nèi)容、更新時間戳
- 前端打包的時候向manifest.json寫入當前時間戳信息
- 在入口文件main.js中引入檢查版本更新的邏輯,有更新則提示更新。有兩種方式提示提示用戶:
路由守衛(wèi)router.beforeResolve(Vue-Router為例)
,檢查更新,對比manifest.json文件的響應頭Etag判斷是否有更新- 通過Worker輪詢,檢查更新,對比manifest.json文件的響應頭Etag判斷是否有更新。Worker線程并不影響其他線程的邏輯。
整體邏輯如下所示:
1、public目錄下新建manifest.json
{ "timestamp":21312321311, "msg":"更新內(nèi)容如下:\n--1.添加系統(tǒng)更新提示機制" }
2、寫入當前時間戳到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('讀取文件時出錯:', err) return } // 將文件內(nèi)容轉換JSON const dataObj = JSON.parse(data) //修改時間戳 dataObj.timestamp = new Date().getTime() // 將修改后的內(nèi)容寫回文件 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 //創(chuàng)建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 函數(shù)發(fā)起了一個 HTTP 請求,獲取了指定資源的頭信息(HTTP 頭部)。其中 manifest.json 是要請求的資源,Date.now() 會生成當前時間的時間戳,作為查詢參數(shù) v 的值,這樣可以避免瀏覽器緩存,強制獲取最新的資源。請求方式為 HEAD,這意味著只請求資源的頭部信息而不獲取具體的內(nèi)容。
let etag = response.headers.get('etag')
,從 HTTP 響應中獲取了 ETag 頭部信息,ETag 是服務器生成的資源唯一標識,用于檢查資源是否發(fā)生了變化。具體比較邏輯如下:
1、客戶端發(fā)起請求,請求中包含上次獲取的資源的ETag。
2、服務器收到請求后,比較客戶端提供的ETag與當前資源的ETag是否一致。
3、如果一致,則返回HTTP 304 Not Modified響應,表示資源未發(fā)生變化,客戶端可以使用緩存的版本。
4、如果不一致,服務器返回最新的資源內(nèi)容,同時更新ETag。
5、客戶端收到響應后,更新本地緩存的資源內(nèi)容和ETag。
(3)路由跳轉檢測版本更新
// 路由攔截 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)收到版本更新信息,進行彈框提示
收到版本更新信息后,先暫停輪詢檢查版本更新,點擊確定按鈕,則發(fā)送destory消息,點擊取消按鈕則發(fā)送recheck消息
async function confirmReload(msg = '', lastEtag) { worker && worker.postMessage({ type: 'pause' }) try { //彈框提示邏輯 } catch (e) { } }
checkUpdate.js 全部代碼實現(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 { // 檢測前端資源是否有更新 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)檢查版本更新
邏輯更第三步差不多,唯一一點就是檢測到更新后,發(fā)送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)監(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,不滿足條件,錯失刷新時機 // checkUpdate() intervalId = setInterval(()=>{ checkUpdate() //這里3s方便測試 }, 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 { // 檢測前端資源是否有更新 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,不滿足條件,錯失刷新時機 // 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() }
如果配置有問題,會一直出現(xiàn)下面這個問題,worker-loader 的配置要放在其他 loader 前。
總結
到此這篇關于前端項目部署后如何提示用戶版本更新的文章就介紹到這了,更多相關前端提示用戶版本更新內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
JS利用ES6和ES5分別實現(xiàn)長整數(shù)和字節(jié)數(shù)組互轉
這篇文章主要為大家詳細介紹了長整數(shù)與字節(jié)數(shù)組互轉的技術原理,文中提供了ES6(現(xiàn)代瀏覽器/Node.js)與ES5(兼容舊環(huán)境)兩套實現(xiàn)方案,需要的可以參考下2025-04-04js中document.getElementById(id)的具體用法
本文主要介紹了js中document.getElementById(id)的具體用法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2023-04-04使用json來定義函數(shù),在里面可以定義多個函數(shù)的實現(xiàn)方法
下面小編就為大家?guī)硪黄褂胘son來定義函數(shù),在里面可以定義多個函數(shù)的實現(xiàn)方法。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2016-10-10JavaScript canvas復刻蘋果發(fā)布會環(huán)形進度條
canvas 真是一個好東西,它給前端插上了想象的翅膀,伴隨著 h5 而來,將 web 代入了新的領域。本文將利用anvas復刻蘋果發(fā)布會環(huán)形進度條,感興趣的可以嘗試一下2022-07-07js采用concat和sort將N個數(shù)組拼接起來的方法
這篇文章主要介紹了js采用concat和sort將N個數(shù)組拼接起來的方法,涉及JavaScript針對數(shù)組的合并與排序操作相關技巧,需要的朋友可以參考下2016-01-01