@mingto/version-polling
v1.0.35
Published
实时检测是否发布新版本
Readme
@mingto/version-polling
版本轮询检测工具,用于自动检测应用版本更新。
特性
- 🔄 自动轮询:支持定时轮询检测新版本
- ⚡ 本地存储:自动缓存最新版本,减少重复检查
- 🎯 灵活配置:支持自定义轮询间隔、请求参数、版本比较规则
- 📦 TypeScript 支持:完整的类型定义
- 🔔 事件驱动:支持检测到新版本时触发回调
- ✨ 智能跳过:根据配置自动跳过指定版本
- 🛠️ 手动控制:支持手动触发检测与停止检测
- 🚫 防抖动:内置防抖机制,避免频繁触发检测
安装
# npm
npm install @mingto/version-polling
# yarn
yarn add @mingto/version-polling
# pnpm (推荐)
pnpm add @mingto/version-polling快速开始
import { createVersionPolling } from '@mingto/version-polling'
// 创建实例
const versionPolling = createVersionPolling({
// 获取版本信息的接口
fetchVersion: async () => {
const response = await fetch('/api/version')
const data = await response.json()
return {
version: data.version, // 版本号,如 '1.2.3'
updateContent: data.content, // 更新内容描述
force: data.force, // 是否强制更新
downloadUrl: data.downloadUrl, // 下载地址
}
},
// 当前版本
currentVersion: '1.0.0',
// 轮询间隔,单位毫秒(默认 5分钟)
interval: 5 * 60 * 1000,
// 是否立即执行一次检测
immediate: true,
})
// 监听版本更新事件
versionPolling.on('update', (versionInfo) => {
console.log('发现新版本:', versionInfo.version)
console.log('更新内容:', versionInfo.updateContent)
// 这里可以弹出更新提示
})
// 开始轮询
versionPolling.start()
// 停止轮询
// versionPolling.stop()
// 手动检测一次
// versionPolling.check()API 文档
创建实例
createVersionPolling(options)
创建版本轮询检测实例。
import { createVersionPolling } from '@mingto/version-polling'
const versionPolling = createVersionPolling({
fetchVersion: async () => {
// 获取版本信息
const response = await fetch('/api/version')
return response.json()
},
currentVersion: '1.0.0',
interval: 5 * 60 * 1000,
immediate: true,
})配置参数 options:
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|------|------|------|--------|------|
| fetchVersion | () => Promise<VersionInfo> | ✅ | - | 获取版本信息的异步函数 |
| currentVersion | string | ✅ | - | 当前应用版本 |
| interval | number | ❌ | 300000 | 轮询间隔,单位毫秒(默认 5 分钟) |
| immediate | boolean | ❌ | true | 是否在开始时立即执行一次检测 |
| versionKey | string | ❌ | 'version' | 版本号在返回结果中的键名 |
| storageKey | string | ❌ | 'version_polling_last_version' | 本地存储的键名 |
| enableLog | boolean | ❌ | true | 是否启用日志输出 |
| compareVersions | (localVersion: string, remoteVersion: string) => boolean | ❌ | 内置版本比较函数 | 自定义版本比较函数 |
版本信息对象 VersionInfo
| 属性 | 类型 | 说明 |
|------|------|------|
| version | string | 版本号 |
| updateContent | string | 更新内容描述 |
| force | boolean | 是否强制更新 |
| downloadUrl | string | 下载地址 |
| [key: string] | any | 其他自定义字段 |
实例方法
versionPolling.start()
开始轮询检测版本更新。
versionPolling.start()versionPolling.stop()
停止轮询检测。
versionPolling.stop()versionPolling.check()
手动触发一次版本检测,返回 Promise。
const hasUpdate = await versionPolling.check()
if (hasUpdate) {
console.log('发现新版本!')
} else {
console.log('当前已是最新版本')
}versionPolling.getLatestVersion()
获取最近一次检测到的最新版本信息。
const latestVersion = versionPolling.getLatestVersion()
console.log('最新版本:', latestVersion)versionPolling.on(eventName, callback)
监听事件,支持链式调用。
versionPolling
.on('update', (versionInfo) => {
console.log('发现新版本:', versionInfo)
})
.on('noUpdate', () => {
console.log('当前已是最新版本')
})
.on('error', (error) => {
console.error('检测版本出错:', error)
})versionPolling.off(eventName, callback?)
移除事件监听,如果不传回调则移除该事件的所有监听。
// 移除指定回调
versionPolling.off('update', updateCallback)
// 移除该事件的所有回调
versionPolling.off('update')versionPolling.setInterval(interval)
动态设置轮询间隔。
// 设置为 10 分钟轮询一次
versionPolling.setInterval(10 * 60 * 1000)versionPolling.setCurrentVersion(version)
动态更新当前版本号。
versionPolling.setCurrentVersion('1.2.3')事件说明
| 事件名 | 触发时机 | 回调参数 | 说明 |
|--------|----------|----------|------|
| update | 检测到有新版本时 | VersionInfo | 发现可以更新时触发 |
| noUpdate | 检测后没有新版本时 | - | 当前版本已是最新版本 |
| error | 检测过程中发生错误时 | Error | 请求失败或其他异常 |
| check | 每次执行检测时 | - | 开始执行检测前触发 |
| stop | 停止轮询时 | - | 调用 stop 方法后触发 |
使用示例
基础使用
import { VersionPolling } from '@mingto/version-polling'
const versionPolling = new VersionPolling({
fetchVersion: async () => {
const response = await fetch('/api/version')
return response.json()
},
currentVersion: '1.0.0',
interval: 5 * 60 * 1000,
immediate: true,
})
versionPolling
.on('update', (info) => {
// 显示更新提示
alert(`发现新版本 ${info.version}!\n更新内容: ${info.updateContent}`)
})
.on('error', (error) => {
console.error('版本检测失败:', error)
})
versionPolling.start()自定义版本比较规则
import { VersionPolling } from '@mingto/version-polling'
const versionPolling = new VersionPolling({
fetchVersion: async () => {
const response = await fetch('/api/version')
return response.json()
},
currentVersion: '1.0.0',
// 自定义版本比较函数
compareVersions: (local, remote) => {
// 自定义比较逻辑,返回 true 表示需要更新
const localParts = local.split('.').map(Number)
const remoteParts = remote.split('.').map(Number)
for (let i = 0; i < 3; i++) {
if (remoteParts[i] > localParts[i]) return true
if (remoteParts[i] < localParts[i]) return false
}
return false
},
})结合 Vue 使用
<script setup lang="ts">
import { ref, onMounted, onUnmounted } from 'vue'
import { VersionPolling } from '@mingto/version-polling'
const showUpdateModal = ref(false)
const newVersionInfo = ref(null)
let versionPolling = null
onMounted(() => {
versionPolling = new VersionPolling({
fetchVersion: async () => {
const response = await fetch('/api/version')
return response.json()
},
currentVersion: '1.0.0',
interval: 5 * 60 * 1000,
})
versionPolling
.on('update', (info) => {
newVersionInfo.value = info
showUpdateModal.value = true
})
.on('error', (error) => {
console.error('版本检测失败:', error)
})
versionPolling.start()
})
onUnmounted(() => {
versionPolling?.stop()
})
const handleUpdate = () => {
// 跳转到更新页面
window.location.href = newVersionInfo.value.downloadUrl
}
const handleSkip = () => {
showUpdateModal.value = false
}
</script>
<template>
<div v-if="showUpdateModal" class="update-modal">
<h3>发现新版本 {{ newVersionInfo.version }}</h3>
<p>{{ newVersionInfo.updateContent }}</p>
<button @click="handleUpdate">立即更新</button>
<button v-if="!newVersionInfo.force" @click="handleSkip">稍后再说</button>
</div>
</template>在 React 中使用
import { useState, useEffect } from 'react'
import { VersionPolling } from '@mingto/version-polling'
const VersionChecker = () => {
const [showModal, setShowModal] = useState(false)
const [versionInfo, setVersionInfo] = useState(null)
useEffect(() => {
const versionPolling = new VersionPolling({
fetchVersion: async () => {
const response = await fetch('/api/version')
return response.json()
},
currentVersion: '1.0.0',
interval: 5 * 60 * 1000,
})
versionPolling
.on('update', (info) => {
setVersionInfo(info)
setShowModal(true)
})
.on('error', (error) => {
console.error('版本检测失败:', error)
})
versionPolling.start()
return () => {
versionPolling.stop()
}
}, [])
const handleUpdate = () => {
if (versionInfo) {
window.location.href = versionInfo.downloadUrl
}
}
if (!showModal || !versionInfo) {
return null
}
return (
<div className="update-modal">
<h3>发现新版本 {versionInfo.version}</h3>
<p>{versionInfo.updateContent}</p>
<button onClick={handleUpdate}>立即更新</button>
{!versionInfo.force && (
<button onClick={() => setShowModal(false)}>稍后再说</button>
)}
</div>
)
}
export default VersionChecker跳过指定版本
import { VersionPolling } from '@mingto/version-polling'
const versionPolling = new VersionPolling({
fetchVersion: async () => {
const response = await fetch('/api/version')
return response.json()
},
currentVersion: '1.0.0',
// 自定义版本比较逻辑,实现跳过指定版本
compareVersions: (local, remote) => {
const skippedVersions = ['1.1.0', '1.2.0'] // 要跳过的版本列表
if (skippedVersions.includes(remote)) return false
// 正常比较逻辑
const localParts = local.split('.').map(Number)
const remoteParts = remote.split('.').map(Number)
for (let i = 0; i < 3; i++) {
if (remoteParts[i] > localParts[i]) return true
if (remoteParts[i] < localParts[i]) return false
}
return false
},
})带请求头验证
import { VersionPolling } from '@mingto/version-polling'
const versionPolling = new VersionPolling({
fetchVersion: async () => {
const response = await fetch('/api/version', {
method: 'GET',
headers: {
'X-App-Id': 'your-app-id',
'X-Platform': 'web',
},
})
return response.json()
},
currentVersion: '1.0.0',
interval: 5 * 60 * 1000,
})
versionPolling.start()内置版本比较规则
默认的版本比较规则:
- 语义化版本比较:支持
major.minor.patch格式的版本号比较 - 逐项比较:从 major 开始,依次 minor、patch
- 相等返回 false:版本相同时不需要更新
- 支持前导零:如
1.02.03与1.2.3被视为相同版本
// 示例
'1.0.0' vs '1.0.1' // true,需要更新
'1.0.0' vs '1.1.0' // true,需要更新
'1.0.0' vs '2.0.0' // true,需要更新
'1.0.0' vs '1.0.0' // false,无需更新
'1.0.0' vs '0.9.9' // false,无需更新状态说明
| 状态 | 值 | 说明 |
|------|----|------|
| IDLE | 'idle' | 空闲状态,未开始轮询 |
| RUNNING | 'running' | 运行中,正在轮询 |
| CHECKING | 'checking' | 正在执行检测中 |
注意事项
- ⚠️ 清理资源:在组件卸载或页面离开时,务必调用
stop()方法停止轮询,避免内存泄漏 - ⚠️ 轮询间隔:建议设置合理的轮询间隔,过短可能导致服务器压力过大,过长可能导致更新不及时
- ⚠️ 网络错误:网络异常时不会触发
update事件,但会触发error事件,不会影响后续轮询 - ⚠️ 版本格式:建议使用标准的语义化版本号格式(如
1.2.3),便于比较 - ⚠️ 本地缓存:检测到的最新版本会自动缓存到本地,下次启动会自动检查缓存的版本是否需要更新
- ⚠️ 防抖机制:内置防抖,频繁调用
check()不会导致重复请求
常见问题
Q: 如何实现强制更新?
A: 可以在 fetchVersion 返回的结果中设置 force: true,然后在 update 事件回调中根据这个字段判断是否强制用户更新。
Q: 用户关闭页面后再打开还会提示更新吗?
A: 是的,再次打开时会重新检测版本,如果有新版本还是会提示。可以结合本地存储记录用户已跳过的版本来实现忽略该版本功能。
Q: 可以只在应用启动时检测一次吗?
A: 可以,设置 immediate: true 并调用一次 check() 后立即调用 stop(),或者不调用 start() 只使用手动检测方式。
Q: 支持自定义存储吗?
A: 可以通过自定义 fetchVersion 函数实现,在函数内部处理存储逻辑。
Q: 检测到新版本后如何通知用户?
A: 在 update 事件回调中处理:
- 弹窗提示更新
- 展示更新日志
- 跳转到更新页面或应用商店
许可证
MIT
