📌 问题背景
SPA项目采用客户端渲染,页面初始化和渲染在浏览器中完成。用户不刷新页面,仍使用旧的缓存代码。如果项目重新部署,用户感知不到更新。
💡 解决思路
可以通过在路由钩子中添加版本检测代码,比较打包后script文件名的hash,来判断是否有新的部署版本。
如果检测到版本变化,可以在页面中弹出提示,要求用户手动刷新页面,加载最新的代码。
🛠 核心代码
export class Updater {
oldScript: string[] // 存储第一次值也就是script 的hash 信息
newScript: string[] // 获取新的值 也就是新的script 的hash信息
constructor() {
this.oldScript = []
this.newScript = []
this.init() // 初始化
}
async init() {
const html: string = await this.getHtml()
this.oldScript = this.parserScript(html)
}
async getHtml() {
try {
const html = await fetch(`/?_timestamp=${Date.now()}`).then(res => res.text()) // 读取index html
return html
}
catch (error) {
console.error('Error occurred while fetching HTML:', error)
throw error
}
}
parserScript(html: string) {
const reg = /<script(?:\s+[^>]*)?>(.*?)</script\s*>/ig // script正则
const scriptArr = html.match(reg) as string[] // 匹配script标签
return scriptArr
}
findVersion(arr: string[]) {
const result = arr.find(script => /index/i.test(script))
if (result) {
const regex = /index-(.*?).js/
const match = result.match(regex)
if (match && match.length > 1) {
const number = match[1]
return { number }
}
}
}
async checkUpdate() {
try {
const newHtml = await this.getHtml()
const scriptArr = this.parserScript(newHtml)
const number = this.findVersion(scriptArr)
return number
}
catch (error) {
console.error('Error occurred during update check:', error)
}
}
}
🚀 示例
// 创建实例
const updater = new Updater()
router.afterEach((to) => {
updater.checkUpdate().then((result: any) => {
console.log(result)
const version = sessionStorage.getItem('version')
const newVersion = result.number
if (!newVersion) return
if (!version) {
sessionStorage.setItem('version', newVersion)
return
}
if (version !== newVersion) {
updateModal()
sessionStorage.setItem('version', newVersion)
}
})
})
const updateModal = () => {
const key = `open${Date.now()}`
notification.config({
maxCount: 1,
})
notification.open({
duration: null,
icon: () => h(ExclamationCircleOutlined, { style: 'color: #ffa900' }),
message: '发现新版本',
description: '检测到到最新版本,刷新后立即使用',
btn: () => h(Button, {
type: 'primary',
size: 'small',
onClick: () => window.location.reload(),
}, {
default: () => '立即刷新',
}),
key,
onClose: () => {},
})
}