一个用于实时检测 web 应用更新的 JavaScript 库
以下内容是通过 GitHub Copilot 生成的 😊
在现代 web 应用开发中,前端代码的更新频率较高,尤其是单页应用(SPA)。当应用发布新版本时,如何及时通知用户并引导其刷新页面以加载最新资源,成为了一个亟待解决的问题。version-polling
库应运而生,旨在提供一种简单高效的方式来检测前端应用的版本更新,并提示用户进行页面刷新。
用户在浏览器中打开某 web 应用(通常是后台管理系统)很长时间且未刷新页面时,如果应用有新功能添加或问题修复,用户可能无法及时知道有新版发布。这样会导致用户继续使用旧版,影响用户体验和数据准确性,甚至出现程序报错。
-
针对前端 web 单页应用(SPA)而设计
-
纯前端技术实现,使用简单无需后端支持
-
提供三种版本控制方式
1.使用
HTTP ETag
作为版本标识符
2.使用chunkHash
作为版本标识符v1.3.0
3.使用version.json
文件管理版本号v1.3.0
-
支持 TypeScript
使用
HTTP ETag
作为版本标识符来判断应用是否有更新。
HTTP ETag
说明:每次请求index.html
文件时,HTTP 响应头上会有一个 ETag 字段, 格式类似ETag: W/"0815"
该字段的值是服务器资源的唯一标识符,通过比较前后两次请求的 Etag 字段值,可以判断资源是否发生变化,以这个为依据判断是否有更新。
缺点是HTTP ETag
是由服务器生成的,前端不可控。
- 使用
Web Worker
API 在浏览器后台轮询请求index.html
文件,不会影响主线程运行。 - 请求
index.html
文件,对比本地和请求响应头的 ETag 的字段值。 - 如果 ETag 字段值不一致,说明有更新,则弹出更新提示,并引导用户手动刷新页面(例如弹窗提示),完成应用更新。
- 当页面不可见时(例如切换标签页或最小化窗口),停止实时检测任务;再次可见时(例如切换回标签页或还原窗口),恢复实时检测任务。
- 支持添加其他前端事件(例如页面导航或自定义事件)触发检测,由开发者自行决定检测时机。
使用
chunkHash
作为版本标识符来判断应用是否有更新。
chunkHash
说明:因为前端 spa 项目都是打包后再部署,这里以 vite 为例,打包产物 index.html 文件内容中会存在一个 script 标签,格式类似<script type="module" crossorigin src="/assets/index.065a65a6.js"></script>
,其中index
是 chunk 名称,后面的065a65a6
是 chunk 哈希值,每次项目代码有改动再打包这里的chunkHash
哈希值都会发生变化,以这个为依据判断是否有更新。
- 使用
Web Worker
API 在浏览器后台轮询请求index.html
文件,不会影响主线程运行。 - 请求
index.html
文件,对比当前文件和最新文件中的chunkHash
的哈希值。 - 如果
chunkHash
哈希值不一致,说明有更新,则弹出更新提示,并引导用户手动刷新页面(例如弹窗提示),完成应用更新。 - 其他逻辑和方式一保持一致。
使用
version.json
文件管理版本内容,由开发者手动控制应用版本更新。 缺点是需要开发者手动维护version.json
文件
- 使用
Web Worker
API 在浏览器后台轮询请求version.json
文件,不会影响主线程运行。 - 请求
version.json
文件,对比当前文件和最新文件中的 version 字段值。 - 版本号比较遵循 Semver 语义化版本规范,如果有高版本则弹出更新提示,并引导用户手动刷新页面(例如弹窗提示),完成应用更新。
- 其他逻辑和方式一保持一致。
适用于支持原生 ES Modules 的浏览器
chrome >= 87
edge >= 88
firefox >= 78
safari >= 14
- 通过 npm 引入,并通过构建工具进行打包
# 本地项目安装
npm install version-polling --save
- 通过 CDN 方式引入,直接插入到 HTML
无侵入用法,接入成本最低
<script src="https://unpkg.com/version-polling/dist/version-polling.min.js"></script>
当检测到有新版本时,会触发onUpdate
回调函数,弹出提示用户有更新,点击确定刷新页面。
// 在应用入口文件中使用: 如 main.js, app.jsx
import { createVersionPolling } from 'version-polling';
createVersionPolling({
silent: process.env.NODE_ENV === 'development', // 开发环境下不检测
onUpdate: (self) => {
const result = confirm('页面有更新,点击确定刷新页面!');
if (result) {
self.onRefresh();
} else {
self.onCancel();
}
},
});
import { createVersionPolling } from 'version-polling';
import { MessageBox } from 'element-ui';
createVersionPolling({
silent: process.env.NODE_ENV === 'development', // 开发环境下不检测
onUpdate: (self) => {
MessageBox.confirm('检测到网页有更新, 是否刷新页面加载最新版本?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
})
.then(() => {
self.onRefresh();
})
.catch(() => {
self.onCancel();
});
},
});
如果觉得前端轮询请求太频繁,可以设置轮询间隔为半小时。
import { createVersionPolling } from 'version-polling';
createVersionPolling({
pollingInterval: 30 * 60 * 1000, // 每 30 分钟检测一次
silent: process.env.NODE_ENV === 'development', // 开发环境下不检测
onUpdate: (self) => {
const result = confirm('页面有更新,点击确定刷新页面!');
if (result) {
self.onRefresh();
} else {
self.onCancel();
}
},
});
在浏览器页面导航跳转时、页面显示切换时,触发检测任务。
在Window上触发的事件。
import { createVersionPolling } from 'version-polling';
createVersionPolling({
eventTriggerList: ['popstate'],
silent: process.env.NODE_ENV === 'development', // 开发环境下不检测
silentPollingInterval: true,
onUpdate: (self) => {
const result = confirm('页面有更新,点击确定刷新页面!');
if (result) {
self.onRefresh();
} else {
self.onCancel();
}
},
});
由开发者自行决定触发检测的时机。
import { createVersionPolling } from 'version-polling';
createVersionPolling({
eventTriggerList: ['myEvent'],
silent: process.env.NODE_ENV === 'development', // 开发环境下不检测
onUpdate: (self) => {
const result = confirm('页面有更新,点击确定刷新页面!');
if (result) {
self.onRefresh();
} else {
self.onCancel();
}
},
});
通过dispatchEvent
触发版本检测。
dispatchEvent(new CustomEvent('myEvent'));
还可以在路由跳转时触发,以Vue Router
为例,借助导航守卫来触发版本检测。
router.afterEach((to, from) => {
dispatchEvent(new CustomEvent('myEvent'));
});
使用chunkHash
作为版本标识符,控制应用版本更新。以打包构建工具 vite 为例。
import { createVersionPolling } from 'version-polling';
createVersionPolling({
vcType: 'chunkHash',
silent: import.meta.env.MODE === 'development', // 开发环境下不检测
onUpdate: (self) => {
const result = confirm('页面有更新,点击确定刷新页面!');
if (result) {
self.onRefresh();
} else {
self.onCancel();
}
},
});
chunkName 也可能是其他值,例如 Vue CLI 是app
,查看打包产物里index.html
文件内容来确定。
使用version.json
文件管理版本号,控制应用版本更新。
import { createVersionPolling } from 'version-polling';
createVersionPolling({
vcType: 'versionJson',
silent: import.meta.env.MODE === 'development', // 开发环境下不检测
onUpdate: (self) => {
const result = confirm('页面有更新,点击确定刷新页面!');
if (result) {
self.onRefresh();
} else {
self.onCancel();
}
},
});
修改version.json
文件,配置version
字段值
{
"version": "2.1.0",
"versionContent": "更新内容: 修复已知存在的问题"
}
version.json
文件跟index.html
文件放在同一个服务器目录下。
参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
vcType | 版本控制方式,可选值有etag 、chunkHash 、versionJson |
string |
etag |
htmlFileUrl | index.html 文件地址 |
string |
${location.origin}${location.pathname} |
chunkName | chunk 名称 | string |
index |
versionFileUrl | version.json 文件地址 |
string |
${location.origin}${location.pathname}version.json |
eventTriggerList | 触发版本检测的事件名称列表 | string[] |
- |
pollingInterval | 轮询间隔,单位为毫秒,默认为 5 分钟 | number |
5 * 60 * 1000 |
silent | 为true 时,不进行版本检测 |
boolean |
false |
silentPollingInterval | 为true 时,不做轮询版本检测 |
boolean |
false |
silentPageVisibility | 为true 时,visibilitychange 事件不会触发版本检测 |
boolean |
false |
onUpdate | 检测到版本更新触发的回调函数 | (self) => void |
- |
v1.3.0
移除appETagKey
、forceUpdate
配置项,移除原因使用鸡肋