產(chǎn)生原因:
使用vite對(duì)項(xiàng)目進(jìn)行打包兽埃,對(duì) js 和 css 文件使用了 chunkhash 進(jìn)行了文件緩存控制,但是項(xiàng)目的index.html文件在版本頻繁迭代更新時(shí)适袜,會(huì)存在被瀏覽器緩存的情況柄错。
在發(fā)版后,如果用戶不強(qiáng)制刷新頁(yè)面,瀏覽器會(huì)使用舊的index.html文件鄙陡,在跳轉(zhuǎn)頁(yè)面時(shí)會(huì)向服務(wù)器端請(qǐng)求了上個(gè)版本 chunkhash 的 js 和 css 文件冕房,但此時(shí)的文件已經(jīng)在版本更新時(shí)已替換刪除了,最終表現(xiàn)為頁(yè)面卡頓趁矾,控制臺(tái)報(bào)錯(cuò) 404耙册。
解決方案:
方案1
服務(wù)器發(fā)版時(shí),上一個(gè)版本的asset文件包不刪除
缺點(diǎn):隨著頻繁發(fā)版毫捣,服務(wù)器端前端項(xiàng)目文件會(huì)越來(lái)越多详拙,浪費(fèi)空間;若舊頁(yè)面的接口涉及到參數(shù)改動(dòng)等蔓同,會(huì)引起報(bào)錯(cuò)饶辙;流水線使用 docker 打包部署會(huì)變得非常麻煩。
方案2:
在每一次打包時(shí)斑粱,前端生成一個(gè)version版本號(hào)弃揽,路由跳轉(zhuǎn)時(shí)請(qǐng)求服務(wù)端的version.json數(shù)據(jù),與本地緩存的版本號(hào)做對(duì)比则北,從而監(jiān)控版本迭代變化矿微,實(shí)現(xiàn)自動(dòng)更新。
缺點(diǎn):在實(shí)現(xiàn)自動(dòng)更新的過(guò)程尚揣,沒(méi)辦法跳到指定的頁(yè)面涌矢,需要刷新后再重新點(diǎn)擊。但這個(gè)體驗(yàn)感可以忽略不計(jì)快骗。
具體操作步驟如下:
- 在項(xiàng)目目錄下創(chuàng)建build文件
// build\build.js
console.log("build > 文件開(kāi)始執(zhí)行娜庇!")
const fs = require("fs")
const path = require("path")
function getRootPath(...dir) {
return path.resolve(process.cwd(), ...dir)
}
const runBuild = async () => {
try {
const OUTPUT_DIR = "dist"
const VERSION = "version.json"
const versionJson = {
version: "V_" + Math.floor(Math.random() * 10000) + Date.now()
}
fs.writeFileSync(getRootPath(`${OUTPUT_DIR}/${VERSION}`), JSON.stringify(versionJson))
console.log(`version file is build successfully!`)
} catch (error) {
console.error("version build error:\n" + error)
process.exit(1)
}
}
runBuild()
console.log("build > 文件執(zhí)行結(jié)束!")
- 在
package.json
中配置"postbuild": "node ./build/build.js"
方篮,不同環(huán)境的構(gòu)建都要配置
"postbuild": "node ./build/build.js",
"postbuild:test": "node ./build/build.js",
"postbuild:pre": "node ./build/build.js",
- 封裝本地緩存版本號(hào)的讀寫(xiě)方法
export const getAppVersion = () => {
return localStorage.getItem(CacheKey.APP_VERSION) as string
}
export const setAppVersion = (version: string) => {
localStorage.setItem(CacheKey.APP_VERSION, version)
}
- 寫(xiě)一個(gè)公共方法名秀,用于檢驗(yàn)服務(wù)端版本號(hào)是否更新,如有更新則自動(dòng)刷新
// 檢查服務(wù)端是否已經(jīng)更新藕溅,如果更新刷新頁(yè)面
export async function checkAppNewVersion() {
if (process.env.NODE_ENV === "development") {
return
}
// 帶參數(shù)是為了拿到最新的數(shù)據(jù)
const url = `/version.json?t=${Date.now()}`
let res = null
try {
res = await axios.get(url)
} catch (err) {
console.error("checkAppNewVersion error: ", err)
}
if (!res) return
const version = res.data.version
const localVersion = getAppVersion()
if (localVersion && localVersion == version) {
return
}
if (localVersion && localVersion !== version) {
ElMessage({
message: "發(fā)現(xiàn)新內(nèi)容匕得,自動(dòng)更新中...",
type: "success",
showClose: true,
duration: 1500,
onClose: () => {
setAppVersion(version)
window.location.reload()
}
})
}
setAppVersion(version)
}
- 在
router.beforeEach
鉤子中,請(qǐng)求服務(wù)器version版本號(hào)
router.beforeEach(async (to, _from, next) => {
await checkAppNewVersion()
...
})
- 為了防止當(dāng)用戶在登錄頁(yè)時(shí)蜈垮,服務(wù)端更新了版本,所以用戶在輸入用戶名和密碼之后裕照,點(diǎn)擊登錄攒发,顯示登錄成功,正常是應(yīng)該直接跳到首頁(yè)晋南,但是此時(shí)跳轉(zhuǎn)前檢測(cè)到版本更新惠猿,所以會(huì)原地刷新一下獲取最新資源,導(dǎo)致頁(yè)面又恢復(fù)到了登錄之前的狀態(tài)负间,用戶需要重新登錄偶妖〗啵可以在
App.vue
中加入這個(gè)監(jiān)聽(tīng)
// 監(jiān)聽(tīng)頁(yè)面打開(kāi)顯示
document.addEventListener('visibilitychange', function () {
// console.log('show ===>', document.visibilityState, !document.hidden)
if (!document.hidden) {
checkAppNewVersion()
}
})
注意:
如果是采用pnpm打包,需要在.npmrc
中配置:
enable-pre-post-scripts=true