前端项目重新打包部署后如何通知用户更新
前端项目重新打包部署后如何通知用户更新
前端项目重新打包部署后,由于用户没及时更新页面,导致配置存的还是旧逻辑值,引发了线上问题;所以进行版本更新的通知功能就很有必要上马了。
常用的webSocket解决方案
实现思路
- webSocket; 大致逻辑思考应该是前端在部署好后向服务器发送一个状态变更通知;服务器接收后主动向前端push;前端通过心跳检测,接收到相关更新时弹出提示,让用户确认更新;
缺点:
- 需要服务器配合开发;
- 部署好后主动向服务器发送变更通知,但是CI/CD部署是在远端,对于前端来说完全是黑盒子;当然可以尝试去研究docker相关文档应该是有解决方案; 当然也可以选择手段变更状态,在系统哪加个入口点击后向服务器发送通知;
- 整个看下来,成本不小;
纯前端方案
效果
具体提示什么可以自定义,这里是简单的demo
实现思路
每次打包时,都生成一个时间戳,作为系统的伪版本,放到JSON文件中,通过对比文件的响应头Etag判断是否有更新。具体步骤如下:
- 在public文件夹下加入version.json文件,里面存放两个字段:更新内容、更新时间戳
前端打包的时候向version.json写入当前时间戳信息 - 在入口文件main.js中引入检查版本更新的逻辑,有更新则提示更新。
具体实现起来有两种方案:路由拦截、多线程+定时器
在 vite.config.ts 文件中写入时间戳(或者格式化后的时间);
const { readFile, writeFile } = require('fs');
import moment from 'moment';
function pathResolve(dir: string) {
return resolve(__dirname, '.', dir);
}
// 获取版本是否更新文件,这里的路径要配合nginx写 Date.new()避免浏览器缓存,强制获取最新的资源
readFile(resolve(__dirname, 'public/version.json'), (err: any, data: any) => {
if (err) {
console.log('读取失败');
return;
}
const version = JSON.parse(data);
version.lastTime = moment(new Date().getTime()).format('YYYY-MM-DD HH:mm:ss');
writeFile(resolve(__dirname, 'public/version.json'), JSON.stringify(version), (err: any) => {
if (err) {
console.log('写入失败');
return;
}
});
});
路由拦截
// !路由拦截
import router from '/@/router';
let lastEtag: string | null = '';
let hasUpdate = false;
const checkUpdate = async () => {
try {
// 获取版本是否更新文件,这里的路径要配合nginx写 Date.new()避免浏览器缓存,强制获取最新的资源
let response = await fetch(`/baiju/version.json?v=${Date.now()}`, { method: 'head' });
let etag = response.headers.get('etag');
if (!lastEtag) lastEtag = etag;
if (lastEtag && lastEtag !== etag) {
lastEtag = etag;
hasUpdate = true;
}
} catch (error) {
console.error('Error checking for updates:', error);
}
};
// 其实利用浏览器的多线程更好一些,用路由拦截还必须触发路由才可以 worker+setInterval
// 路由拦截
router.beforeEach(async (to, from, next) => {
// 路由跳转前
next();
try {
await checkUpdate();
if (hasUpdate) {
// 提示页面也可以采用worker多线程去处理提示,这里只是演示
alert('发现新版本,请刷新页面');
window.location.reload();
}
} catch (err) {
console.log(err);
}
});
多线程
main.ts中 创建多线程
let worker = new Worker('./worker/checkUpdate.js');
worker.postMessage('checkUpdate');
worker.onmessage = function (event) {
let data = event.data;
console.log(data);
if (data) {
alert('发现新版本,请刷新页面');
window.location.reload();
}
};
多线程逻辑处理
self.onmessage = function (event) {
let data = event.data;
// console.log('Worker:', checkUpdate());
// 30s更新一次 (这个时间我觉得存在争议,如果白天在线用户多就可以频繁一些,等到晚上重新打包,把时间间隔拉长)
let timer = setInterval(() => {
checkUpdate().then((res) => {
self.postMessage(res)
if (res) {
clearInterval(timer);
self.close();
}
})
}, 30 * 1000);
// 先执行一次
checkUpdate().then((res) => {
self.postMessage(res)
if (res) {
clearInterval(timer);
self.close();
}
})
};
let lastEtag = '';
let hasUpdate = false;
const checkUpdate = async () => {
return new Promise(async (resolve, reject) => {
// 获取版本是否更新文件,这里的路径要配合nginx写
let response = await fetch(`/version.json?v=${Date.now()}`, { method: 'head' });
let etag = response.headers.get('etag');
if (!lastEtag) lastEtag = etag;
if (lastEtag && lastEtag !== etag) {
lastEtag = etag;
hasUpdate = true;
}
resolve(hasUpdate);
});
};