npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@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()

内置版本比较规则

默认的版本比较规则:

  1. 语义化版本比较:支持 major.minor.patch 格式的版本号比较
  2. 逐项比较:从 major 开始,依次 minor、patch
  3. 相等返回 false:版本相同时不需要更新
  4. 支持前导零:如 1.02.031.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' | 正在执行检测中 |

注意事项

  1. ⚠️ 清理资源:在组件卸载或页面离开时,务必调用 stop() 方法停止轮询,避免内存泄漏
  2. ⚠️ 轮询间隔:建议设置合理的轮询间隔,过短可能导致服务器压力过大,过长可能导致更新不及时
  3. ⚠️ 网络错误:网络异常时不会触发 update 事件,但会触发 error 事件,不会影响后续轮询
  4. ⚠️ 版本格式:建议使用标准的语义化版本号格式(如 1.2.3),便于比较
  5. ⚠️ 本地缓存:检测到的最新版本会自动缓存到本地,下次启动会自动检查缓存的版本是否需要更新
  6. ⚠️ 防抖机制:内置防抖,频繁调用 check() 不会导致重复请求

常见问题

Q: 如何实现强制更新?

A: 可以在 fetchVersion 返回的结果中设置 force: true,然后在 update 事件回调中根据这个字段判断是否强制用户更新。

Q: 用户关闭页面后再打开还会提示更新吗?

A: 是的,再次打开时会重新检测版本,如果有新版本还是会提示。可以结合本地存储记录用户已跳过的版本来实现忽略该版本功能。

Q: 可以只在应用启动时检测一次吗?

A: 可以,设置 immediate: true 并调用一次 check() 后立即调用 stop(),或者不调用 start() 只使用手动检测方式。

Q: 支持自定义存储吗?

A: 可以通过自定义 fetchVersion 函数实现,在函数内部处理存储逻辑。

Q: 检测到新版本后如何通知用户?

A: 在 update 事件回调中处理:

  • 弹窗提示更新
  • 展示更新日志
  • 跳转到更新页面或应用商店

许可证

MIT