一:需求
页面打包上传到服务器后,用户能在自己电脑上立即感知到更新
二:需求分析
调研后得知:
1.靠浏览器缓存,各种catche啥的不靠谱. -----pass,缓存不会那么快生效
2.页面JS定时去查询服务器保存版本号的文件,查看版本号是否过期
---pass,效率太差,如果有十万用户,每个用户五分钟去服务器查询一次,服务器每
五分钟被查询五十万次.
三:实现方案
前端接口请求http-header 携带时间戳版本号,后端比对时间戳版本号.
如果请求带过来的时间戳小于服务器保存的,返回版本号过期错误.
如果请求带过来的时间戳大于服务器保存的时间戳,更新服务器时间戳版本号.
同时,如果请求没带版本号参数,则不对版本号进行校验,方便dev环境前端开发.
前端最好能自动生成版本号,自动更新,不用手动填值.
如果版本号过旧就执行JS代码location.reload(true),但是上线后有的人的浏览器
执行了location.reload(true) 或者ctrl+f5,有可能部分文件还是走的缓存.
所以弹窗让用户选择更新还是取消.如果选取消就清空版本号,后端不校验空的就行
四:具体代码应用
样例代码为vue2+webpack. 主要都是JS代码. vue3和react也可以参考
第一步修改 入口文件
在入口文件index.html创建一个同级别JS文件config.js
在index.html引入这个文件
<script src="config.js?version=123456789"></script>
index.html引入文件后面有个后缀?version=123456789,为什么要带这个后缀呢?
因为不带部分浏览器会给你缓存了,哪怕用户ctrl+f5也不行,只能手动清理缓存.
这个后缀的数字123456789每次打包后都会被修改,为了方便就修改成版本号
第二步 创建config.js
代码如下,里面代码不重要,主要起到模板记录作用,因为每次打包会重新生成.
主要意思就是读取版本号变量timestamp 的值,并存入localStorage中,
这样接口请求的时候可以从localStorage读取timestamp 的值放到head带给后端.
let timestamp = 1693386173000
if(localStorage.timestamp){
if(timestamp < localStorage.timestamp){
console.log('timestamp is small')
}else{
console.log('timestamp is big')
localStorage.timestamp = timestamp
}
}else{
localStorage.timestamp = timestamp
}
console.log('configjs read again')
上面代码除了注释可以删,每一行都有用,谨慎修改
3.修改vue.config.js
我们要在输入npm run build打包命令后,进行操作
引入node的文件函数,能读取文件
const fs = require("fs");
// 判断是否为生产环境
const isProd = process.env.NODE_ENV === "production";
然后在plugins下面放入同级函数
const Timestamp = new Date().getTime(); //时间戳
if (isProd) {
fs.readFile("./public/config.js", "utf8", function(err, data) {
if (err) {
console.log(err)
return
}
// console.log(data)
try {
// data = data.replace('1693971026219',Timestamp)
data = "";
let c_1 = " let timestamp = " + Timestamp;
console.log("c_1", c_1);
data = c_1 + "\n" + data;
let c_3 = "\n"+"console.log('configjs read again')"
let c_4 = `
if(localStorage.timestamp){
if(timestamp < localStorage.timestamp){
console.log('timestamp is small')
}else{
console.log('timestamp is big')
localStorage.timestamp = timestamp
}
}else{
localStorage.timestamp = timestamp
}
`
data = data +"\n"+String(c_4) + c_3
console.log('result_data',data)
} catch (err2) {
console.log(err2);
}
fs.writeFile("./public/config.js", data, function(err) {
if (err) {
console.log(err)
}
console.log("configjs文件已修改");
});
});
fs.readFile("./public/index.html", "utf8", function(err, data_2) {
if (err) {
console.log(err)
return
}
// console.log(data)
try {
data_2 = data_2.replace('123456789',Timestamp)
} catch (err2) {
console.log(err2);
}
fs.writeFile("./public/index.html", data_2, function(err) {
if (err) {
console.log(err)
}
console.log("html文件已修改");
// console.log("data_2",data_2)
});
});
}
该函数主要两个作用:
一是修改config.js文件,把里面的timestamp值换成最新的时间戳值
二是修改index.html文件,把config.js?version=123456789的后缀123456789换成时间戳,避免浏览器缓存
第三步 vue实例绑定弹窗
这步不是必须的,因为要在axios的配置文件拦截响应,如果报版本号过期,要弹窗提示用户是否更新页面.
博主用的UI组件是antdvue 1.X,如果想在JS文件用vue的弹窗插件,需要先把这个弹窗插件绑定在vue实例上.
所以在app.vue执行如下操作,给vue实例加个弹窗绑定.
弹窗记得要JS防抖.
mounted(){
// console.log('app vue')
// console.log(this.$confirm)
Vue.$confirm = this.$confirm
}
第四步 校验版本号
和后端约定,如果版本号旧了,在返回的data对象里code属性报402.
在axios的配置文件,相应拦截回调函数里配置402错误对应的执行代码.
记得按照你和后端的约定修改代码
response => {
if (
response.data.code != 200 &&
response.request.responseType != "arraybuffer" &&
response.data.info !== "成功"
) {
//版本旧了
if (response.data.code == 402) {
console.log('行为分析 接口报402 错误码')
// location.reload(true)
if (timer_request) {
clearTimeout(timer_request)
}
timer_request = setTimeout(() => {
console.log(88945)
// localStorage.timestamp = ''
// location.reload(true)
Vue.$confirm({
title: '检测到新版本,是否立即更新页面?',
content: h =>(
<div style="">
点击取消,后续将不再检测版本,
除非手动更新页面或者等待浏览器自动更新缓存后才继续检测
</div>
) ,
onOk() {
console.log('OK');
localStorage.timestamp = ''
location.reload(true)
},
onCancel() {
console.log('Cancel');
localStorage.timestamp = ''
},
});
}, 4000)
}
return response;
}
return response;
},
第五步 打包上线
1.如果你是自动部署,比如把代码提交到git,jekines自动部署,代码直接提上去就行
2.如果你是本机打包好后,手动把打包文件传到服务器部署.
那么每次执行打包操作,你的index.html和config.js都会被修改,生成打包文件后记得还原下