1.目标场景
有时候上完线,用户还停留在老的页面,用户不知道网页重新部署了,跳转页面的时候有时候 js 连接 hash 变了导致报错跳不过去,并且用户体验不到新功能。
2.思考解决方案
如何去解决这个问题
思考中...
如果后端可以配合的话可以使用 webSocket 跟后端进行实时通讯,前端部署完之后,后端给个通知,前端检测到 Message 进行提示,还可以在优化一下使用 EvnentSource 这个跟 socket 很像只不过他只能后端往前端推送消息,前端无法给后端发送,也不需要给后端发送。
重新进行思考...
讨论得出了一个方案,在项目根目录给个 json 文件,写入一个固定的 key 值然后打包的时候变一下,然后代码中轮询去判断看有没有变化,有就提示。
但是写完之后发现太麻烦了,需要手动配置 json 文件,还需要打包的时候修改,有没有更简单的方案,
进行第二轮讨论。
第二轮讨论的方案是根据打完包之后生成的 script src 的 hash 值去判断,每次打包都会生成唯一的 hash 值,只要轮询去判断不一样了,那一定是重新部署了.
3.代码实现
interface Options {
timer?: number;
}
export class Updater {
oldScript: string[]; //存储第一次值也就是script 的hash 信息
newScript: string[]; //获取新的值 也就是新的script 的hash信息
dispatch: Record<string, Function[]>; //小型发布订阅通知用户更新了
constructor(options: Options) {
this.oldScript = [];
this.newScript = [];
this.dispatch = {};
this.init(); //初始化
this.timing(options?.timer); //轮询
}
async init() {
const html: string = await this.getHtml();
this.oldScript = this.parserScript(html);
}
async getHtml() {
const html = await fetch("/").then((res) => res.text()); //读取index html
return html;
}
parserScript(html: string) {
const reg = new RegExp(/<script(?:\s+[^>]*)?>(.*?)<\/script\s*>/gi); //script正则
return html.match(reg) as string[]; //匹配script标签
}
//发布订阅通知
on(key: "no-update" | "update", fn: Function) {
(this.dispatch[key] || (this.dispatch[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.dispatch["no-update"].forEach((fn) => {
fn();
});
} else {
//否则通知更新
this.dispatch["update"].forEach((fn) => {
fn();
});
}
}
timing(time = 10000) {
//轮询
setInterval(async () => {
const newHtml = await this.getHtml();
this.newScript = this.parserScript(newHtml);
this.compare(this.oldScript, this.newScript);
}, time);
}
}
代码用法
//实例化该类
const up = new Updater({
timer:2000
})
//未更新通知
up.on('no-update',()=>{
console.log('未更新')
})
//更新通知
up.on('update',()=>{
console.log('更新了')
})
4.测试
执行 npm run build 打个包
安装http-server
使用http-server 开个服务
重新打个包npm run build
这样子就可以检测出来有没有重新发布就可以通知用户更新了。