项目技术栈:vue2+js(老项目)
最近在修bug的时候,总会被测试追着问更新了没有,但总是修完了,转头就继续忙别的事情去了,所以就想弄一个功能,在前端部署更新的时候,在项目上弹出相应的提示,告诉他们,更新了需要刷新一下才能获取最新的资源。(由于还是比较常规的,webpack打包,然后部署到服务上)
思路:
- 包的时候,都去更新本地的版本号
- 主线程放一个定时获取版本号
- 览器存的版本号定时做比对(其实跟轮询差不多),然后通知用户是否刷新当前资源。
定好思路就开始干啦!
part1:
1.在public目录下放新建一个版本文件,就叫version.json吧
内容的话,就定义初始版本号
{ "version": 1 }
2.在.gitignore文件忽略我们的版本文件
添加 /public/*.json
3.(划重点)在打包的时候,执行更新的脚本,同时也要规避一下在自己本地开发的时候无需更新版本号,在执行npm run build (vue-cli-service-build) 的时候进行更新版本号。
在vue.config.js文件中,通过chainWebpack(通过链式编程的形式,来修改默认的 webpack 配置)这个方法,去添加脚本修改我们的version.json文件
- 修改文件,咱们先引入node的fs模块
const fs = require("fs"); - 在
chainWebpack方法里,先判断当前构建时执行的命令,打印console.log(process.argv);
- 根据这个可以判断在build的时候,去执行我们更新的脚本。
- 思路就是通过fs的readFile方法,去读取
version.json文件,要是没有版本文件,咱们直接writeFileSync,直接生成一个版本文件,要是存在,咱们就读取其版本内容,并进行更新 - 我这里定义的版本号是从1开始自增的数字
代码:
fs.readFile("./public/version.json", "utf8", (err, data) => {
if (!err) {
let obj;
try {
obj = JSON.parse(data);
obj.version++;
} catch (error) {
obj = {
version: 1
};
}
// 在 JSON 序列化成功才会去同步修改 version.json 文件内容
if (obj)
fs.writeFileSync("./public/version.json", JSON.stringify(obj));
// 没有找到文件,抛出异常
} else {
fs.writeFileSync(
"./public/version.json",
JSON.stringify({
version: 1
})
);
}
});
这样,更新版本号部分就可以啦
作为部署的部分,不用担心因版本号更新的问题,导致拉取代码失败,后补方案也可以通过强拉代码去解决
part2:
获取版本号更新部分
还是老生常谈的方法,定时获取的版本号和本地缓存的版本号进行比较,根据结果是否显示更新弹窗。
// 创建一个定时控制器
let timeFun = (function () {
let timeId = null;
function start(fun) {
if (timeId == null) {
timeId = setInterval(fun, 60 * 60 * 1000);
}
}
// 销毁当前定时器
function stop() {
clearInterval(timeId);
timeId = null;
}
return { start, stop };
})();
由于,首次进入页面的时候,获取的资源是最新的,所以,首次进入,仅记录最新版本号,并不进行刷新弹窗提示。
代码:
const handler = () => {
axios.get(location.origin + "/version.json").then(res => {
// 获取当前版本号
const version = localStorage.getItem("version");
// 判断是否首次进入页面,首次进入直接更新版本号,不用提示
const isFirstLoad = window.performance.navigation.type === 1;
console.log(res.version, version, isFirstLoad);
if (version != res.version && isFirstLoad) {
// 暂停当前定时器,避免重叠后重复弹窗
timeFun.stop();
MessageBox.confirm("发现新版本,是否更新?", "提示", {
closeOnClickModal: false,
closeOnPressEscape: false,
showClose: false,
confirmButtonText: "更新",
cancelButtonText: `${1}小时后提醒我!`,
type: "warning",
})
.then(() => {
MessageBox.close();
// 更新版本号
localStorage.setItem("version", res.version);
location.reload();
})
.catch(() => {
MessageBox.close();
timeFun.start(handler);
});
} else {
localStorage.setItem("version", res.version);
}
})
};
timeFun.start(handler)
基本是这样就实现了
后续:
考虑到主线程的占用问题,该计时器一直在运行,且不释放,因此这是一个其中一个弊端吧。
为了
根据web-worker的相关资料,在vue下使用web-worker
建造一个执行脚本,定时获取版本号,并通过主线程传入的数据进行比对,最终是否给主线程发送更新通知
首先先创建一个脚本文件
// worker.js
let currentVersion = 0;
let timeId = null;
async function getVersion(e) {
try {
// 查阅了web-worker文档,支持fetch
const response = await fetch(location.origin + "/version.json");
const data = await response.json();
const latestVersion = data.version;
console.log(latestVersion, currentVersion);
if (latestVersion !== currentVersion) {
// 版本号有更新,发送通知
console.log(data);
postMessage(data.version);
}
} catch (error) {
console.error("Failed to get version:", error);
}
}
// setInterval(getVersion, timeout);
// 监听主线程发送过来的数据
addEventListener("message", ({ data }) => {
if (data.type === "update") {
console.log("update", data);
currentVersion = data.version;
timeId = setInterval(getVersion, data.timeout || 60 * 60000);
}
if (data.type === "handon") {
clearInterval(timeId);
}
if (data.type === "destory") {
removeEventListener("message");
}
});
在主线程引用该脚本
由于worker的引入路径问题,因此需要借助一些worker-loader在打包的时候解析好引入路径的问题
// App.vue
import Worker from "worker-loader!./worker.js";
methods: {
startWorker() {
this.worker = new Worker();
this.worker.postMessage({
version: Number(localStorage.getItem("version")),
type: "update",
});
this.worker.onmessage = ({ data }) => {
// 判断是否首次进入页面,首次进入直接更新版本号,不用提示
const isFirstLoad = window.performance.navigation.type === 1;
if (isFirstLoad) {
this.worker.postMessage({
type: "handon",
});
MessageBox.confirm("发现新版本,是否更新?", "提示", {
closeOnClickModal: false,
closeOnPressEscape: false,
showClose: false,
confirmButtonText: "更新",
cancelButtonText: `${1}小时后提醒我!`,
type: "warning",
})
.then(() => {
MessageBox.close();
// 更新版本号
localStorage.setItem("version", data);
location.reload();
})
.catch(() => {
MessageBox.close();
this.worker.postMessage({
version: Number(localStorage.getItem("version")),
type: "update",
});
});
} else {
localStorage.setItem("version", res.version);
}
};
},
stopWorker() {
this.worker.postMessage({
type: "destory",
});
this.worker.terminate();
this.worker = null;
},
},
created() {
this.startWorker();
},
beforeDestroy() {
if (this.worker) {
// 销毁子线程
this.stopWorker();
}
},
以上记录一些,反思和不断更新的过程。如有建议,望各路大神指教指教!