当我们重新部署前端项目的时候,如果用户一直停留在页面上并未刷新使用,会存在功能使用差异性的问题,因此,当前端部署项目后,需要提醒用户有去重新加载页面。在以往解决方案中,不少人会使用websocket去通知客户端更新,但是为了这么个小功能加入websocket是十分不明智的,新方案的思路是去轮询请求index.html文件,从中解析里面的js文件,由于vue打包后每个js文件都有指纹标识,因此可以对比每次打包后的指纹,分析文件是否存在变动,如果有变动即可提示用户更新。
原理
let lastSrcs; //上一次获取到的script地址 const scriptReg = /<script.*src=["'](?<src>[^"']+)/gm; async function extractNewScripts() { const html = await fetch('/standard?_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(); } } autoRefresh(); }, DURATION) } autoRefresh();引入
// 引入自动更新提醒 import "@/utils/auto-update.js"使用element-ui的notify提示更新
let lastSrcs; //上一次获取到的script地址 const scriptReg = /<script.*src=["'](?<src>[^"']+)/gm; async function extractNewScripts() { const html = await fetch('/standard?_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); } autoRefresh(); }, DURATION) } autoRefresh();编写模板
//堆代码 duidaima.com <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>使用
// 引入 import CnNotify from "@common/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, }, }), }); }); }, },