前言

用户在访问单页面网站时,如果生产环境已经发布了新的版本(有功能上的变化),由于单页面中路由特性或浏览器缓存的原因,并不会重新加载前端资源,此时用户浏览器所并非加载是最新的代码,从而可能遇到一些 bug。

那么,在我们部署之后,如何提醒用户版本更新,并引导用户刷新页面呢?

解决方案

这里用的【轮询】的方式请求index.html文件,从中解析里面的js文件,由于vue打包后每个js文件都有指纹标识,因此对比每次打包后的指纹,分析文件是否存在变动,如果有变动则提示用户更新!

环境:vue3 + ts + vite + element-plus

步骤

1.在utils文件夹下新建auto-update.ts,内容如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62

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-rollupoptionsConfiguration Options

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/** 
* @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方法。

1
2
3
4
import { autoRefresh } from "@/utils/auto-update"
if (import.meta.env.MODE == 'production') {
autoRefresh()
}

效果

结语

如果你有更好的方案,记得在评论去留言喔~