一.场景需求
用户在使用我们的web项目的时候,由于有时候会有生产环境的更新,如果用户在更新前就加载了网页,更新之后用户没有刷新的话偶尔会导致页面报错,这种往往我们提示用户刷新一下就好了,不属于业务或者代码层面的错误。近期由于多次出现这种情况,故老大叫我解决一下。
二.解决思路
做版本号管理,每次调用接口的时候做一次版本校验,如果校验通过则正常访问接口,如果校验不通过就返回特殊状态码前端自动做一次刷新页面以获取最新的页面静态资源。
三.实现
首先,需要每次打包的时候将版本号变动起来,也就是自动去更改版本号,这里通过打包时执行一段js的方式来实现
package.json
{
"name": "project",
"version": "0.0.11",
"lastBuildTime": "2023-02-24 17:28:52",
"scripts": {
"dev": "vite",
"start": "vite",
"build": "vite build --mode=production",
"build:auto": "node ./myBuild.js && vite build --mode=production",
"build:stag": "vite build --mode=staging",
"serve": "vite preview"
}
}
myBuild.js
const fs = require("fs")
const dayjs = require("dayjs")
const axios = require("axios")
// 构建版本号的函数,这里只简单做下递增,后面可以优化成每构建一定次数的版本之后几句自动给二级版本号加1
const versionGenerrator = (version) => {
let newVersion = Number(version[2]);
console.log([version[0], version[1], ++newVersion].join("."));
return [version[0], version[1], ++newVersion].join(".");
};
// 设置版本号--传入版本号并保存至数据库,本地也重新生成package.json的文件
const setVersion = (version) => {
// 构造传入后端接口的参数
let data = {
id: 59,
paramKey: "version_web",
paramValue: version,
};
try {
axios
.post(
"https://gateway.xxxxxxx.com/platform/config/updateByKey",
data,
{
headers: {
"Content-Type": "application/json; charset=UTF-8",
},
}
)
.then((response) => {
if (response.data?.code == 200 && response.data?.data == 1) {
console.log("文件版本号修改成功,开始写入本地文件");
packageJson.version = version;
packageJson.lastBuildTime = dayjs().format("YYYY-MM-DD HH:mm:ss");
fs.writeFile(
"./package.json",
JSON.stringify(packageJson, null, "\t"),
(err) => {
if (err) {
console.log("写入文件失败,请检查是否拥有文件夹权限", err);
} else {
console.log("文件写入成功-->" + packageJson.lastBuildTime);
}
}
);
}
});
} catch (error) {
console.log("版本号写入接口失败,已停止");
}
};
// 先读取package.json
let packageJson = JSON.parse(fs.readFileSync("./package.json"));
// 构建新的版本号
let version = versionGenerrator(packageJson.version.split("."));
// 调用接口设置到后端并且声成新的package.json
setVersion(version);
到这里就已经实现了版本号自动变更提交到服务器,接下来就是每次调用接口对这个版本号进行校验,我选择的是直接将校验的动作放在网关上面,校验不通过的时候就返回一个505的状态码。前端需要做的就是在调用接口的时候将当前的版本号放在headers里面,这个操作就放在咱们已经封装好的axios请求拦截器跟响应拦截器上面了
// 请求拦截
service.interceptors.request.use(
(config) => {
// JWT鉴权处理
if (store.getters["user/token"]) {
config.headers["Token"] = store.state.user.token;
}
config.headers["version_web"] = version; //版本号,从package.json中引入
return config;
},
(error) => {
return Promise.reject(error);
}
);
// 响应拦截
service.interceptors.response.use(
(response) => {
if(response.code==505){
ElMessage.error("检查到版本号不一致自动刷新页面");
setTimeout(() => {
window.location.reload();
}, 3000);
}
}
);
这里就完全实现了这个功能了,客户浏览器如果加载的是0.0.1版本的静态文件,数据库里面的版本号是0.0.2,调用接口就会返回505并且自动重载页面。不过后面跟我们后端的架构讨论,他认为这种实现方式对后端的开销太大了,说是最好使用发布订阅模式,我不是很清楚这种场景怎么去做订阅,也不知道市面上主流的解决方案是什么我想到了这种方式就用这种方式来实现了。如果有懂的小伙伴请一定要私信我,有偿求助。