转自公众号:前端帮
背景
我经常使用的一个云开发网站LAF,为了每次用起来方便,我是从来都不关闭这个标签页的,有时候进入页面,就发现一个弹窗,提示我有新版本,点击即可更新。
我还纳闷pc端网站怎么会有这样的功能,压根就不需要啊(内心OS:这产品**吧)!后面一想,现在的网站基本都是基于SPA开发的,跳转页面不会导致页面刷新,这样的话有了新版本时不刷新页面就无法更新。更何况还有我这种从不关闭某个标签页的,原来小丑是我自己~
技术揭秘
猜想可能的实现方案:
- 使用websocket,有新版本时后端告诉前端,或者用SSE实时通知前端。
- 让后端写个接口,每次前端部署更新了,后端同步更新版本号,前端轮询拿到最新版本号,和本地旧版本号对比。
其实仅看这两种,个人感觉第二种反而比第一种要好,没必要为了一个版本更新用上服务端推送。
因为版本号在后端就是一个静态数据,且传输数据量小到可以忽略不计,所以即使用轮询,对服务器和网络压力也不高。
说这么多,不如直接打开f12,看看人家是怎么做的。惭愧,network里没找到,可能人家就是用的推送吧,需要的时候才会推送。
纯前端实现
题外话:生活在大数据时代,有时候真是感觉自己被监控了,想啥来啥,这不,第二天就刷到渡一袁老师的视频,讲的就是这个自动检测更新。
思路
SPA项目都是只有一个html入口文件,通过引用一堆js css来实现所有的功能,那是不是只要检测到我们当前访问的这个html文件和服务端的不一样了,就认为是需要更新了。对,就是这样。那怎么检测呢?
实现
建个vue3项目演示一下
npm create vue@latest
npm run dev跑一下开发环境,没问题。
直接运行npm run build打包,我在本地起一个nginx,指向这个项目的dist目录
访问一下auto-update.localhost,没问题。
注意看地址栏,是打包后的代码了。
src下建一个auto-update.js,在main.js里引入
import './auto-update.js'
以下代码逻辑非常简单,且有详细的注释:
// 保存旧的html代码字符串
let oldHtml;
// 从服务端获取最新的html代码
async function fetchHtml() {
// fetch('/index.html')获取的就是html文件,
// 再用text()转为字符串
// 加上时间戳避免缓存
return await fetch(`/index.html?timestamp=${+new Date()}`)
.then(res => res.text())
}
// 判断需不需要更新
async function needUpdate() {
const newHtml = await fetchHtml()
// 默认不需要更新
let result = false
if (oldHtml && oldHtml !== newHtml) {
// 有旧值,并且新旧值不同,才需要更新
result = true
}
// 没有旧值,或者新旧值一样,就无需更新
// 无论用户更不更新版本,都更新旧值,
// 这样当用户选择不更新时就不会再提示了
// 除非刷新页面
oldHtml = newHtml
return result
}
// 递归调用,3秒一次
function autoRefresh() {
setTimeout(async () => {
if (await needUpdate()) {
console.log('需要更新')
const res = confirm('有新版本,点击确定更新')
if (res) {
location.reload()
}
}
autoRefresh()
}, 3000)
}
autoRefresh()
打包部署后,来测试一下。
可以看到不需要用户操作,且纯前端就做到了实时检测更新。当然我这里服务器就在本地,实际会有一个部署到服务器的过程。
这种做法适用于以下三种情况:
-
html文件内没有过多固定代码,即所有的css和js都通过打包工具打包到单独的文件中(有的人喜欢在index.html里写一些全局的固定代码),且没有很多分包的情况,毕竟分包越多,引入的就越多,整个index.html文件就越大。
-
推荐需要经常更新静态代码的网站使用,不经常更新的就没必要了。
-
对实时更新需求强烈的网站使用