前言
当我们重新部署前端项目的时候,如果用户一直停留在页面上并未刷新使用,会存在功能使用差异性的问题,因此,当前端部署项目后,需要提醒用户有去重新加载页面。
技术框架
vue、js、webpack
解决方案
- 编译项目时动态生成一个记录版本号的文件
- 轮询(20s、自己设定时间)这个文件,判断版本号,有新版本则通知用户刷新页面
- 通过监听visibilitychange事件,在页面隐藏时停止轮询,页面显示立马检测一次更新
- 检测到更新后,停止轮询
(感兴趣的可去看方案二:根据打完包之后生成的script src 的hash值去判断
,每次打包都会生成唯一的hash值,只要轮询去判断不一样了,那一定是重新部署了。)
效果
页面右下角提示更新:
代码实现
Step1:在 vue.config.js 实现动态创建版本号文件
if (process.env.VUE_APP_ENV !== "production") {
// 这里我设置的是只在非生产环境自动检测更新(生成version);
// 想要所有环境都自动检测更新,只要写if(process.env.VUE_APP_ENV !== "production")内的内容就好
const { writeFile, existsSync } = require('fs')
// 动态生成版本号
const createVersion = () => {
//检测目录是否存在
if (existsSync('./public')) {
writeFile(`./public/version.json`, `{"version":"${Date.now()}"}`, (err) => {
if (err) {
console.log('写入version.json失败')
console.log(err)
} else {
console.log('写入version.json成功')
}
})
} else {
setTimeout(createVersion, 1000)
}
}
setTimeout(createVersion, 10000)
}
Step2:在src目录下封装 auto-update.js
/*
* @Description: 自动更新
*/
let currentVersion // 当前版本
let version // 新版本
// const timeData = 60 * 1000 // 检查间隔时间
const timeData = 20 * 1000 // 检查间隔时间
let hidden = false // 页面是否隐藏
let setTimeoutId
let needTip = true // 默认开启提示
// 获取版本号
const getVersion = async () => {
return fetch('/version.json?timestep=' + Date.now()).then((res) => res.json())
}
// 检查更新
const checkUpdate = async () => {
console.log('***************checkUpdate**************')
const currentVersion = sessionStorage.getItem("version")
version = (await getVersion()).version
// 本地没有 version,表示刚进入系统,直接塞值
if (!currentVersion) return sessionStorage.setItem("version", version)
console.log("🚀 ~ file: auto-update.js:19 ~ version:", version)
console.log("🚀 ~ file: auto-update.js:21 ~ currentVersion:", currentVersion)
console.log("🚀 ~ file: auto-update.js:23 ~ Number(version) !== Number(currentVersion):", Number(version) !== Number(currentVersion))
let needRefresh = false
if (Number(version) !== Number(currentVersion)) {
console.log('%c 发现新版本~~~~~~', 'color: red')
needRefresh = true
}
return needRefresh
}
// 自动更新
const autoUpdate = async () => {
setTimeoutId = setTimeout(async () => {
// 页面隐藏了就不检查更新
if (!hidden) {
const willUpdate = await checkUpdate()
console.log("🚀 ~ file: auto-update.js:71 ~ setTimeoutId=setTimeout ~ willUpdate, version:", willUpdate, version)
if (willUpdate && needTip) {
// 延时更新,防止部署未完成用户就刷新空白
setTimeout(()=>{
// ----弹框确认---先简单点弹框确认,可以用注释内的,跳过右下角通知的内容(Step3、4)
// const result = confirm('发现新版本,点击确定更新')
// if (result) {
// sessionStorage.setItem('version', version)
// location.reload() // 刷新当前页
// }
// --------------
//*****右下角通知提示 */
window.dispatchEvent(
new CustomEvent("onmessageUpdate", {
detail: {
msg: "发现系统版本更新,请刷新页面~",
version: version
},
})
)
//******************* */
}, 10000)
needTip = false // 关闭更新提示,防止重复提醒
}
}
console.log("🚀 ~ file: auto-update.js:90 ~ autoUpdate ~ needTip: ", needTip)
if (needTip) {
console.warn('needTip autoUpdate');
autoUpdate()
}
}, timeData)
}
// 停止检测更新
const stop = () => {
if (setTimeoutId) {
clearTimeout(setTimeoutId)
setTimeoutId = ''
}
}
// 开始检查更新
const start = async () => {
// currentVersion = (await getVersion()).version
autoUpdate()
// 监听页面是否隐藏
document.addEventListener('visibilitychange', () => {
hidden = document.hidden
console.log("🚀 ~ file: auto-update.js:64 ~ document.addEventListener ~ hidden, needTip:", hidden, needTip)
// 页面隐藏了就不检查更新。或者已经有一个提示框了,防止重复提示。
if (!hidden && needTip) {
autoUpdate()
} else {
stop()
}
})
}
export default { start }
Step3:编写模板 CnNotify.vue 文件
<template>
<div class="cn_notify">
<div class="content">
<i class="el-icon-message-solid"></i>
{{ msg }}
</div>
<div class="footer">
<el-row class="btnBox">
<el-button type="primary" @click="onSubmit">确认刷新</el-button>
<el-button @click="cancle">我知道了</el-button>
</el-row>
</div>
</div>
</template>
<script>
export default {
props: {
msg: {
type: String,
default: '',
},
version: {
type: String,
default: '',
},
},
data() {
return {};
},
created() {},
methods: {
// 点击确定更新
onSubmit() {
sessionStorage.setItem('version', this.version) // 存入版本version
location.reload() // 刷新
},
// 关闭
cancle() {
this.$parent.close();
},
},
};
</script>
<style lang='scss' scoped>
.cn_notify {
.content {
padding: 20px 0;
}
.footer {
display: flex;
justify-content: center;
}
}
</style>
<style lang='scss'>
.versionNotifyStyle {
.el-notification__content {
width: 280px !important;
}
}
</style>
Step4:app.vue 使用组件CnNotify
<template>
<div id="app">
<router-view />
</div>
</template>
<script>
// 引入CnNotify组件
import CnNotify from "@/components/common/CnNotify/index.vue"
export default {
name: 'App',
components: {
CnNotify, // 注册组件
},
mounted() {
this.watchUpdate()
},
methods: {
watchUpdate() {
window.addEventListener("onmessageUpdate", (res) => {
console.log("🚀 ~ file: App.vue:20 ~ window.addEventListener ~ res:", res)
let msg = res.detail.msg,
version = res.detail.version
this.$notify({
title: "版本更新提示",
duration: 0,
position: "bottom-right",
dangerouslyUseHTMLString: true,
message: this.$createElement("CnNotify", {
// 使用自定义组件
ref: "CnNotify",
props: {
msg: msg,
version: version
},
}),
customClass:'versionNotifyStyle', //自定义类名
})
})
},
},
}
</script>
Step5:在 main.js 内使用
// 引入自动更新提醒
import autoUpdate from './auto-update'
// 非生产环境使用
process.env.VUE_APP_ENV !== 'production' && autoUpdate.start()