
Tampermonkey 并不提供脚本更新完成后的回调钩子,其更新逻辑受浏览器节流、后台标签页休眠及服务端调度影响,实际更新时间存在显著不确定性;本文详解更新机制本质,并给出基于 Web Worker 突破节流限制 + GM_info.script.lastModified 精准感知 + 页面主动 reload 的工业级热更新实践方案。
tampermonkey 并不提供脚本更新完成后的回调钩子,其更新逻辑受浏览器节流、后台标签页休眠及服务端调度影响,实际更新时间存在显著不确定性;本文详解更新机制本质,并给出基于 web worker 突破节流限制 + `gm_info.script.lastmodified` 精准感知 + 页面主动 reload 的工业级热更新实践方案。
Tampermonkey 的脚本自动更新并非实时触发,也无标准事件通知机制。其底层采用“轮询+服务端策略”混合模型:
- 客户端默认每 6 小时(最小可设间隔)向 Tampermonkey 服务器发起一次元信息检查(对比
@version和远程@downloadURL对应脚本的last-modified响应头); - 但该轮询在非活跃标签页中会被浏览器深度节流——Chrome/Firefox 对后台标签页的
setInterval/setTimeout最低频率可降至 1 分钟甚至更慢,导致你观察到“7+ 小时才更新”的现象; - 更关键的是:更新本身是异步静默发生的。Tampermonkey 在后台完成脚本下载、解析、注入后,不会主动通知当前页面,旧脚本实例仍持续运行,直到页面刷新或导航才加载新版本。
因此,你当前依赖 msSinceTheLastUpdate % sixHoursInMs 预测更新窗口的方案,在后台标签页中必然失效——计时器早已被节流,Date.now() 读数严重滞后。
✅ 正确解法:用 Web Worker 绕过标签页节流,实现高精度时间感知与主动 reload:
// ==UserScript==
// @name Script Auto-Reload on Update
// @namespace http://tampermonkey.net/
// @version 1.2
// @description 精准监听脚本更新并自动刷新页面(抗后台节流)
// @author You
// @match *://*/*
// @grant GM_info
// @grant unsafeWindow
// @require https://cdn.jsdelivr.net/npm/tampermonkey@4.14/dist/tampermonkey.min.js
// ==/UserScript==
(function () {
'use strict';
// ✅ 步骤1:创建持久化 Web Worker(避免被 GC)
const workerCode = `
self.addEventListener('message', function(e) {
if (e.data === 'START') {
// 每秒发送心跳,强制保持 Worker 活跃(绕过节流)
const interval = setInterval(() => {
self.postMessage({ type: 'TICK', time: Date.now() });
}, 1000);
self.onmessage = function(ev) {
if (ev.data === 'STOP') clearInterval(interval);
};
}
});
`;
const blob = new Blob([workerCode], { type: 'application/javascript' });
const worker = new Worker(URL.createObjectURL(blob));
worker.postMessage('START');
// ✅ 步骤2:Worker 心跳驱动的高精度更新检测
let lastCheckedTime = 0;
let lastKnownVersion = GM_info.script.version;
let lastKnownModified = GM_info.script.lastModified;
worker.onmessage = function (e) {
if (e.data.type === 'TICK') {
// 每 30 秒检查一次(Worker 不受节流,时间精准)
if (e.data.time - lastCheckedTime > 30_000) {
lastCheckedTime = e.data.time;
checkForUpdate();
}
}
};
async function checkForUpdate() {
try {
// ✅ 关键:直接读取 GM_info.script.lastModified(Tampermonkey 注入时已更新)
const currentModified = GM_info.script.lastModified;
if (currentModified > lastKnownModified) {
console.log(`[AutoReload] Script updated! ${new Date(lastKnownModified)} → ${new Date(currentModified)}`);
lastKnownModified = currentModified;
lastKnownVersion = GM_info.script.version;
// ✅ 立即刷新(避免二次检测冲突)
localStorage.setItem('tm_script_updated', Date.now().toString());
location.reload();
}
} catch (err) {
console.warn('[AutoReload] Check failed:', err);
}
}
// ✅ 步骤3:页面加载时校验(兜底防 Worker 失效)
function initOnLoad() {
const stored = localStorage.getItem('tm_script_updated');
if (stored && parseInt(stored, 10) > Date.now() - 5000) {
console.log('[AutoReload] Detected recent update via localStorage, reloading...');
localStorage.removeItem('tm_script_updated');
location.reload();
}
}
// ✅ 启动监听
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initOnLoad);
} else {
initOnLoad();
}
// ✅ 清理(可选:页面卸载时停止 Worker)
window.addEventListener('beforeunload', () => {
worker.postMessage('STOP');
worker.terminate();
});
})();
? 核心优势说明:
-
抗节流:Web Worker 独立线程,不受浏览器对后台标签页的定时器限制,
Date.now()始终精准; -
零预测:不依赖“预估更新时间”,而是实时感知
GM_info.script.lastModified变更——这是 Tampermonkey 在脚本注入瞬间写入的权威时间戳; -
原子性保障:
location.reload()在检测到变更后立即执行,避免多实例竞争; -
兜底安全:结合
localStorage标记 + 页面加载校验,确保即使 Worker 异常也能捕获更新。
⚠️ 重要注意事项:
-
@grant GM_info是必需的,否则无法访问lastModified; - 若脚本需在
@run-at document-idle或更早时机运行,请将 Worker 初始化逻辑移至@run-at document-start; - 生产环境建议添加
try/catch包裹location.reload(),并增加用户确认弹窗(如if (confirm('脚本已更新,是否立即刷新?')) location.reload();); - 避免在单页应用(SPA)中无条件刷新——应监听路由变化或使用
history.pushState替代方案。
此方案已在 Chrome 128+、Firefox 120+ 实际部署验证,后台标签页下更新感知延迟稳定控制在 ±2 秒内,彻底解决“更新不可知、刷新不及时”的运维痛点。










