背景
当周五晚上我正准备享用美味的宵夜时,突然接到了一个紧急消息,组里的前端系统出现了bug。我皱起了眉头,放下手中的美食,立即打开了钉钉,查看了具体情况。
原来,这个bug实际上是前几天才解决过的问题。我用手指在屏幕上滑动,果然发现了解决之道——只需简单地刷新页面。原因竟是用户一直停留在页面上,而新版本发布后,他们没有及时刷新页面,导致无法获取到最新的资源。
这让我想起了一个有趣的小故事:有一次,一个小村庄里的农夫决定要建造一个全新的小桥,以便于村民们更便利地过河。他精心设计了桥梁,使用了最先进的材料。然而,当桥梁完工后,他发现村民们仍然习惯于原来的狭窄破旧的木桥,而不愿意使用新桥。原来,他们根本不知道有新桥的存在,因为从来没有人告诉过他们。
解决方案
-
添加manifest.json文件记录版本信息:这是一个很好的做法,可以方便地记录前端应用的版本信息。
-
打包时写入当前时间戳信息:这种方式可以确保每次打包都会更新
manifest.json
中的版本信息,以便后续的版本比较。 -
引入检查更新逻辑:在入口JS中引入检查更新的逻辑是非常好的,这样可以确保应用启动时就会进行版本检查。
-
路由守卫检查更新:使用路由守卫进行检查更新是一个不错的选择,因为它可以确保在用户每次导航到页面时都会进行检查。
-
使用Worker轮询检查更新:这种方法也可以,但需要注意轮询的频率,过于频繁的轮询可能会给服务器带来不必要的负担。而且使用Worker可能会增加一些复杂性。
案列
- public文件夹下创建一个manifest.json文件,用于记录版本信息。在public文件夹下新建manifest.json文件,并写入以下内容:
{
"timestamp": 0,
"msg": "这是一个示例更新提示信息"
}
- 修改public/index.html文件,将标签中的
修改为你项目的名称,并在标签中添加以下代码,用于引入manifest.json文件:
<link rel="manifest" href="<%= BASE_URL %>manifest.json" />
- 在Vue项目中的入口文件src/main.js中,引入检查更新的逻辑:
import Vue from 'vue'
import App from './App.vue'
import router from './router'
Vue.config.productionTip = false
// 引入检查更新逻辑
import '@/utils/checkUpdate'
new Vue({
router,
render: h => h(App)
}).$mount('#app')
- 创建一个工具类文件src/utils/checkUpdate.js,用于实现检查更新的逻辑:
import router from '@/router'
import { Modal } from 'ant-design-vue'
if (process.env.NODE_ENV === 'production') {
let lastEtag = ''
let hasUpdate = false
let worker = null
async function checkUpdate() {
try {
let response = await fetch(`/manifest.json?v=${Date.now()}`, {
method: 'head'
})
let etag = response.headers.get('etag')
hasUpdate = lastEtag && etag !== lastEtag
lastEtag = etag
} catch (e) {
return Promise.reject(e)
}
}
async function confirmReload(msg = '', lastEtag) {
worker &&
worker.postMessage({
type: 'pause'
})
try {
Modal.confirm({
title: '温馨提示',
content: '系统后台有更新,请点击“立即刷新”刷新页面\n' + msg,
okText: '立即刷新',
cancelText: '5分钟后提示我',
onOk() {
worker.postMessage({
type: 'destroy'
})
location.reload()
},
onCancel() {
worker &&
worker.postMessage({
type: 'recheck',
lastEtag: lastEtag
})
}
})
} catch (e) {}
}
router.beforeResolve(async (to, from, next) => {
next()
try {
await checkUpdate()
if (hasUpdate) {
worker.postMessage({
type: 'destroy'
})
location.reload()
}
} catch (e) {}
})
worker = new Worker(
new URL('../worker/checkUpdate.worker.js', import.meta.url)
)
worker.postMessage({
type: 'check'
})
worker.onmessage = ({ data }) => {
if (data.type === 'hasUpdate') {
hasUpdate = true
confirmReload(data.msg, data.lastEtag)
}
}
}
- 创建一个Worker文件src/worker/checkUpdate.worker.js,用于实现Worker的逻辑:
let lastEtag
let hasUpdate = false
let intervalId = ''
async function checkUpdate() {
try {
let response = await fetch(`/manifest.json?v=${Date.now()}`, {
method: 'get'
})
let etag = response.headers.get('etag')
let data = await response.json()
hasUpdate = lastEtag !== undefined && etag !== lastEtag
if (hasUpdate) {
postMessage({
type: 'hasUpdate',
msg: data.msg,
lastEtag: lastEtag,
etag: etag
})
}
lastEtag = etag
} catch (e) {
return Promise.reject(e)
}
}
addEventListener('message', ({ data }) => {
if (data.type === 'check') {
checkUpdate()
intervalId = setInterval(checkUpdate, 5 * 60 * 1000)
}
if (data.type === 'recheck') {
hasUpdate = false
lastEtag = data.lastEtag
intervalId = setInterval(checkUpdate, 5 * 60 * 1000)
}
if (data.type === 'pause') {
clearInterval(intervalId)
}
if (data.type === 'destroy') {
clearInterval(intervalId)
close()
}
})
以上就是一个简单的案例,你可以在Vue项目中测试一下自动检查更新并提示用户刷新页面的功能。记得在实际项目中根据具体情况做适当的调整和优化。