概述
用户在访问单页面网站时,如果生产环境已经发布了新的版本(有功能上的变化),由于单页面中路由特性或浏览器缓存的原因,并不会重新加载前端资源,此时用户浏览器所并非加载是最新的代码,从而可能遇到一些 bug。因此,部署之后,需要提醒用户版本更新,并引导用户刷新页面。
方案
使用轮询的方式请求index.html
文件,从中解析里面的js
文件,由于工程项目打包后每个js
文件都有指纹标识
(打包工具自动处理了文件hash,打包后没有需要配置文件hash),因此对比每次打包后的指纹,分析文件是否存在变动,如果有变动则提示用户更新,流程如下:

实现
新建auto-update.ts
,内容如下
js
import { ElMessageBox } from 'element-plus'
// 上一次获取到的script地址列表,用于比较是否有更新
let initScriptSrcList: string[] = [];
// 正则表达式用于匹配HTML中的script标签的src属性
const scriptReg = /<script.*src=["'](?<src>[^"']+)/gm;
/**
* 从当前页面HTML中提取所有script标签的src地址
* @returns {Promise<string[]>} 返回包含所有script src的数组
*/
const extractNewScripts = async () => {
// 添加时间戳参数避免缓存
const param = Date.now()
// 获取当前页面HTML内容
const html = await fetch('/?_time=' + param).then((resp) => resp.text());
// 重置正则表达式匹配位置
scriptReg.lastIndex = 0;
let result = [];
let match: RegExpExecArray
// 遍历匹配所有script标签的src属性
while ((match = scriptReg.exec(html) as RegExpExecArray) {
result.push(match.groups?.src)
}
return result;
}
/**
* 检查是否有新的脚本更新
* @returns {Promise<boolean>} 返回true表示有更新,false表示无更新
*/
const isUpdate = async () => {
// 获取当前页面所有script的src
const newScripts: string[] = await extractNewScripts();
// 如果是第一次检查,初始化脚本列表并返回无更新
if (!initScriptSrcList.length) {
initScriptSrcList = newScripts;
return false;
}
let res = false;
// 比较脚本数量是否有变化
if (initScriptSrcList.length !== newScripts.length) {
res = true;
}
// 逐个比较脚本地址是否有变化
for (let i = 0; i < initScriptSrcList.length; i++) {
if (initScriptSrcList[i] !== newScripts[i]) {
res = true;
break
}
}
// 更新初始脚本列表为当前最新
initScriptSrcList = newScripts;
return res;
}
// 检查间隔时间(5秒)
const DELAY_TIME = 5000;
/**
* 自动刷新功能入口
* 每隔指定时间检查一次脚本更新,有更新时提示用户刷新页面
*/
export const autoRefresh = () => {
setTimeout(async () => {
// 检查是否需要更新
const needUpdate = await isUpdate();
if (needUpdate) {
console.log('检测到页面有内容更新,自动刷新');
// 使用Element Plus的MessageBox提示用户
ElMessageBox.confirm('检测到页面有内容更新,是否立即刷新?', '更新提示', {
confirmButtonText: '确认',
showCancelButton: false, // 不显示取消按钮
type: 'warning' // 警告类型提示框
}).then(() => {
// 用户确认后刷新页面
location.reload();
})
}
// 递归调用自身,实现持续检查
autoRefresh();
}, DELAY_TIME)
}
使用
我这里使用的是vite+vue的形式,在app.vue文件中
js
<script setup lang="ts">
import HelloWorld from "./components/HelloWorld.vue";
import { onMounted } from "vue";
import { autoRefresh } from "./utils/auto-update";
onMounted(() => {
//生产环境开启检测
if (import.meta.env.MODE == "production") {
autoRefresh();
}
});
</script>