用户在访问单页面网站时,如果生产环境已经发布了新的版本(有功能上的变化),由于单页面中路由特性或浏览器缓存的原因,并不会重新加载前端资源,此时用户浏览器所并非加载是最新的代码,从而可能遇到一些 bug。
那么,在我们部署之后,如何提醒用户版本更新,并引导用户刷新页面呢?
这里用的【轮询】的方式请求index.html
文件,从中解析里面的js
文件,由于vue打包后每个js
文件都有指纹标识
,因此对比每次打包后的指纹,分析文件是否存在变动,如果有变动则提示用户更新!
环境:vue3 + ts + vite + element-plus
1.在utils
文件夹下新建auto-update.ts
,内容如下
import { ElMessageBox } from 'element-plus'
let lastSrcs: any; //上一次获取到的script地址
let needTip = true; // 默认开启提示
const scriptReg = /<script.*src=["'](?<src>[^"']+)/gm;
const extractNewScripts = async () => {
const html = await fetch('/?_timestamp=' + Date.now()).then((resp) => resp.text());
scriptReg.lastIndex = 0;
let result = [];
let match: RegExpExecArray
while ((match = scriptReg.exec(html) as RegExpExecArray)) {
result.push(match.groups?.src)
}
return result;
}
const needUpdate = async () => {
const newScripts = await extractNewScripts();
if (!lastSrcs) {
lastSrcs = newScripts;
return false;
}
let result = false;
if (lastSrcs.length !== newScripts.length) {
result = true;
}
for (let i = 0; i < lastSrcs.length; i++) {
if (lastSrcs[i] !== newScripts[i]) {
result = true;
break
}
}
lastSrcs = newScripts;
return result;
}
const DURATION = 10000;
export const autoRefresh = () => {
setTimeout(async () => {
const willUpdate = await needUpdate();
if (willUpdate) {
// 延时更新,防止部署未完成用户就刷新空白
setTimeout(() => {
ElMessageBox.confirm('检测到页面有内容更新,为了功能的正常使用,是否立即刷新?', '更新提示', {
confirmButtonText: '确认',
showCancelButton: false,
type: 'warning'
}).then(() => {
location.reload();
})
}, 30000);
needTip = false; // 关闭更新提示,防止重复提醒
}
if (needTip) {
autoRefresh();
}
}, DURATION)
}
2.关于vite
的相关配置,这里只放出rollupOptions
相关代码,更多详情请看 build-rollupoptions、Configuration Options
/**
* @description vite document address
* https://vitejs.cn/config/
*/
export default xxx = () => {
return {
base: './',
resolve: {
...
},
server: {
...
},
build: {
rollupOptions: {
output: {
chunkFileNames: 'js/[hash].js', // 引入文件名的名称
entryFileNames: 'js/[hash].js', // 包的入口文件名称
assetFileNames: '[ext]/[hash].[ext]', // 资源文件像 字体,图片等
}
}
}
}
}
3.在入口文件mati.ts
中引入autoRefresh
,如果是生产环境,则执行autoRefresh
方法。
import { autoRefresh } from "@/utils/auto-update"
if (import.meta.env.MODE == 'production') {
autoRefresh()
}
如果你有更好的方案,记得在评论去留言喔~