前端重新部署后如何通知在线用户刷新网页加载最新代码
背景
版本发布上线后,如果用户还停留在老页面,此时用户并不知道网页已经重新部署了,跳转页面的时候可能会出 js 链接 的hash变了导致报错跳不过去的情况,前端需要保证用户的最佳体验。
探讨解决思路
如果后端可以配合前端的话,前端可以使用 webSocket 跟后端实时通讯,前端部署发布完成后,后端发个消息通知,前端检测到 Message 就进行更新提示,或者使用 EventSource, 这跟 webSocket 很像,只不过 EventSource 只能后端推送消息到前端,前端发送消息给后端,当前场景中前端也发送消息给后端。
以上方案需要有后端参与配合,但是当前后台同学腾不出时间来配合实现,所以需要找一个纯前端的解决方案。
以下是我们讨论后得出的两种方案:
1.在项目根目录放一个 json 文件,写入一个固定的key值然后打包的时候变一下,然后代码中轮询去判断看有没有变化,如果有的话就提示更新。但是写完之后发现太麻烦了,需要手动配置json文件,还需要打包的时候修改。
2.第二种方案是根据打完包之后生成的script src 的hash值去判断,每次打包都会生成唯一的hash值,只要轮询去判断不一样了,那一定是重新部署了.
方案2实现
interface Options {
time?: number
}
export class Updater {
oldScripts: string[] //保存当前 script 的 hash 数据
newScripts: string[] //保存最新 script 的 hash 数据
dispatcher: Record<string, Function[]> //发布订阅,通知用户有更新
constructor(options: Options) {
this.oldScripts = [];
this.newScripts = []
this.dispatcher = {}
this.init() //初始化
this.timing(options?.time)//轮询
}
async init() {
const html: string = await this.getHtml()
this.oldScripts = this.parserScript(html)
}
//读取index html
async getHtml() {
const html = await fetch('/').then(res => res.text());
return html
}
//解析 script 标签
parserScript(html: string) {
const reg = new RegExp(/<script(?:\s+[^>]*)?>(.*?)<\/script\s*>/ig) //script正则
return html.match(reg) as string[] //匹配script标签
}
//发布订阅通知
on(key: 'noUpdate' | 'update', fn: Function) {
if(!this.dispatcher[key]){
this.dispatcher[key] = [];
}
this.dispatcher[key].push(fn)
return this;
}
compare(oldArr: string[], newArr: string[]) {
const base = oldArr.length
const arr = Array.from(new Set(oldArr.concat(newArr)))
//新旧 length 一样就是没有更新
if (arr.length === base) {
this.dispatcher['noUpdate'].forEach(fn => {
fn();
});
}
//否则通知更新
else {
this.dispatcher['update'].forEach(fn => {
fn();
});
}
}
timing(time = 15000) {
//轮询
setInterval(async () => {
const newHtml = await this.getHtml()
this.newScripts = this.parserScript(newHtml)
this.compare(this.oldScripts, this.newScripts)
}, time)
}
}
如何使用 Updater 类
//实例化该类
const up = new Updater({
time:2000
})
//未更新通知
up.on('noUpdate',()=>{
console.log('没有更新');
})
//更新通知
up.on('update',()=>{
console.log('有更新');
location.reload();// 重新加载页面
})
测试一下
- 执行 npm run build 第一次打包
- 安装 http-server
- 使用 http-server 启动服务
- 访问应用并停留
- 重新执行 npm run build 再次打包
- 查看浏览器日志打印情况
这样子就可以检测出来有没有重新发布就可以通知用户更新了。