前言
当我们重新部署前端项目的时候,如果用户一直停留在页面上并未刷新使用,会存在功能使用差异性的问题,因此,当前端部署项目后,需要提醒用户有去重新加载页面。
在以往解决方案中,不少人会使用websocket
去通知客户端更新,但是为了这么个小功能加入websocket
是十分不明智的,新方案的思路是去轮询请求index.html
文件,从中解析里面的js
文件,由于vue打包后每个js
文件都有指纹标识
,因此可以对比每次打包后的指纹,分析文件是否存在变动,如果有变动即可提示用户更新
原理
封装函数 auto-update.js
let lastSrcs; //上一次获取到的script地址
let needTip = true; // 默认开启提示
const scriptReg = /<script.*src=["'](?<src>[^"']+)/gm;
async function extractNewScripts() {
const html = await fetch('/?_timestamp=' + Date.now()).then((resp) => resp.text());
scriptReg.lastIndex = 0;
let result = [];
let match;
while ((match = scriptReg.exec(html))) {
result.push(match.groups.src)
}
return result;
}
async function needUpdate() {
const newScripts = await extractNewScripts();
if (!lastSrcs) {
lastSrcs = newScripts;
return false;
}
let result = false;
if (lastSrcs.length !== newScripts.length) {
result = true;
}
for (let i = 0; i < lastSrcs.length; i++) {
if (lastSrcs[i] !== newScripts[i]) {
result = true;
break
}
}
lastSrcs = newScripts;
return result;
}
const DURATION = 5000;
function autoRefresh() {
setTimeout(async () => {
const willUpdate = await needUpdate();
if (willUpdate) {
const result = confirm("页面有更新,请刷新查看");
if (result) {
location.reload();
}
needTip = false; // 关闭更新提示,防止重复提醒
}
if(needTip){
autoRefresh();
}
}, DURATION)
}
autoRefresh();
引入
在main.js中引入
// 引入自动更新提醒
import "@/utils/auto-update.js"
使用element-ui的notify提示更新
修改auto-update.js
let lastSrcs; //上一次获取到的script地址
let needTip = true; // 默认开启提示
const scriptReg = /<script.*src=["'](?<src>[^"']+)/gm;
async function extractNewScripts() {
const html = await fetch('/?_timestamp=' + Date.now()).then((resp) => resp.text());
scriptReg.lastIndex = 0;
let result = [];
let match;
while ((match = scriptReg.exec(html))) {
result.push(match.groups.src)
}
return result;
}
async function needUpdate() {
const newScripts = await extractNewScripts();
if (!lastSrcs) {
lastSrcs = newScripts;
return false;
}
let result = false;
if (lastSrcs.length !== newScripts.length) {
result = true;
}
for (let i = 0; i < lastSrcs.length; i++) {
if (lastSrcs[i] !== newScripts[i]) {
result = true;
break
}
}
lastSrcs = newScripts;
return result;
}
const DURATION = 5000;
function autoRefresh() {
setTimeout(async () => {
const willUpdate = await needUpdate();
if (willUpdate) {
// 延时更新,防止部署未完成用户就刷新空白
setTimeout(()=>{
window.dispatchEvent(
new CustomEvent("onmessageUpdate", {
detail: {
msg: "页面有更新,是否刷新?",
},
})
);
},30000);
needTip = false; // 关闭更新提示,防止重复提醒
}
if(needTip){
autoRefresh();
}
}, DURATION)
}
autoRefresh();
编写模板
CnNotify.vue文件
<template>
<div class="cn_notify">
<div class="content">
<i class="el-icon-message-solid"></i>
{{ msg }}
</div>
<el-row :gutter="20">
<el-col :span="7" :offset="10">
<el-button type="primary" @click="onSubmit">确定</el-button>
</el-col>
<el-col :span="7">
<el-button @click="cancle">取消</el-button>
</el-col>
</el-row>
</div>
</template>
<script>
export default {
props: {
msg: {
type: String,
default: "",
},
},
data() {
return {};
},
created() {},
methods: {
// 点击确定更新
onSubmit() {
location.reload();
},
// 关闭
cancle() {
this.$parent.close();
},
},
};
</script>
<style lang='less' scoped>
.cn_notify {
.content {
padding: 20px 0;
}
.footer {
display: flex;
flex-direction: row-reverse;
}
}
</style>
使用
App.vue
// 引入
import CnNotify from "@/components/CnNotify.vue";
components: {
CnNotify,
},
mounted() {
this.watchUpdate();
},
methods: {
watchUpdate() {
window.addEventListener("onmessageUpdate", (res) => {
let msg = res.detail.msg;
this.$notify({
title: "提示",
duration: 0,
position: "bottom-right",
dangerouslyUseHTMLString: true,
message: this.$createElement("CnNotify", {
// 使用自定义组件
ref: "CnNotify",
props: {
msg: msg,
},
}),
});
});
},
},