
Tampermonkey 脚本更新并非严格定时,而是受浏览器节流、后台标签页休眠、网络条件及扩展自身调度策略影响;无法精确预测更新时刻,但可通过 GM_info.script.lastModified 结合 Web Worker 绕过页面节流,实现高精度检测与无感刷新。
tampermonkey 脚本更新并非严格定时,而是受浏览器节流、后台标签页休眠、网络条件及扩展自身调度策略影响;无法精确预测更新时刻,但可通过 `gm_info.script.lastmodified` 结合 web worker 绕过页面节流,实现高精度检测与无感刷新。
Tampermonkey 的脚本更新逻辑本质上是异步、非实时、且受浏览器运行环境深度制约的后台任务。官方文档明确指出:更新检查由 Tampermonkey 扩展在空闲时段主动触发,最小间隔虽可设为 6 小时(通过设置 → 更新 → 检查频率),但实际执行时间存在显著浮动——尤其当目标标签页处于后台、被浏览器节流(throttling)或系统资源紧张时,JavaScript 定时器(如 setInterval)可能被大幅降频(例如从每秒 1 次降至每分钟数次),导致你当前基于 Date.now() 和 lastModified 的倒计时完全失效。
✅ 正确解法:用 Web Worker 突破节流限制
浏览器对非活跃标签页的 JavaScript 主线程执行施加严格节流,但 Web Worker 不受此限制——它在独立线程中持续运行,即使页面失焦、切换标签甚至最小化,其 setInterval 仍能保持毫秒级精度。这是实现可靠更新感知的唯一稳健路径。
1. 创建独立 Worker 文件(update-worker.js)
// ⚠️ 注意:需托管在同源服务器(或使用 Blob URL,见下文)
self.addEventListener('message', function (e) {
if (e.data === 'START') {
// 每 30 秒向主线程发送心跳,避免被误判为闲置
const heartbeat = setInterval(() => {
self.postMessage({ type: 'HEARTBEAT' });
}, 30_000);
// 核心逻辑:每 5 分钟检查一次脚本版本(精度远超页面端轮询)
const checkInterval = setInterval(() => {
try {
// Tampermonkey 提供的全局对象在 Worker 中不可用 → 需主线程传入最新 lastModified
// 因此我们采用「主线程定期同步状态 + Worker 高频比对」模式
self.postMessage({ type: 'CHECK_UPDATE', timestamp: Date.now() });
} catch (err) {
self.postMessage({ type: 'ERROR', error: err.message });
}
}, 5 * 60 * 1000);
}
});2. 主脚本中集成 Worker 并实现协同更新
// ==UserScript==
// @name Auto-Reload on Script Update
// @namespace https://github.com/yourname
// @version 1.2.0
// @description 使用 Web Worker 精确检测 Tampermonkey 脚本更新并自动刷新
// @author You
// @match *://*/*
// @grant GM_info
// @grant GM_setValue
// @grant GM_getValue
// ==/UserScript==
(function () {
'use strict';
// ✅ 关键:确保 Worker 加载不跨域(推荐 Blob URL 方式,免托管)
const workerCode = `
self.addEventListener('message', function(e) {
if (e.data === 'START') {
const interval = setInterval(() => {
self.postMessage({ type: 'TICK', time: Date.now() });
}, 30000);
}
});
`;
const blob = new Blob([workerCode], { type: 'application/javascript' });
const workerUrl = URL.createObjectURL(blob);
const updateWorker = new Worker(workerUrl);
let lastKnownLastModified = GM_info.script.lastModified;
let updateDetected = false;
// 向 Worker 发送启动指令
updateWorker.postMessage('START');
// 接收 Worker 心跳与检查信号
updateWorker.onmessage = function (e) {
if (e.data.type === 'TICK') {
// 主线程定期同步当前脚本元数据(关键!)
const nowLastModified = GM_info.script.lastModified;
if (nowLastModified > lastKnownLastModified && !updateDetected) {
console.info('[Tampermonkey Auto-Reload] Script updated! Reloading page...');
updateDetected = true;
// ✅ 安全刷新:延迟 1s 确保 DOM 清理完成
setTimeout(() => location.reload(), 1000);
}
lastKnownLastModified = nowLastModified;
}
};
// 清理资源(可选)
window.addEventListener('beforeunload', () => {
updateWorker.terminate();
URL.revokeObjectURL(workerUrl);
});
// ✅ 补充保障:页面加载时校验(防 Worker 启动前已更新)
if (localStorage.getItem('scriptVersion') !== GM_info.script.version) {
console.warn('[Tampermonkey Auto-Reload] Version mismatch on load — forcing reload');
localStorage.setItem('scriptVersion', GM_info.script.version);
setTimeout(() => location.reload(), 500);
}
})();⚠️ 重要注意事项
-
不要依赖
GM_xmlhttpRequest或网络请求判断更新:Tampermonkey 更新是本地文件替换,不触发网络请求,且远程版本号接口(如@updateURL)响应不稳定、易被缓存。 -
避免滥用
location.reload():频繁刷新会中断用户操作。建议添加轻量 UI 提示(如右上角 Toast)并提供手动跳过选项。 -
@grant none脚本无法访问GM_info:务必声明@grant GM_info,否则GM_info.script.lastModified为undefined。 - 生产环境建议增加降级逻辑:当 Worker 不可用(如旧版 Safari)时,回退到 10 分钟级页面轮询,并提示用户启用现代浏览器。
✅ 最佳实践总结
| 场景 | 推荐方案 |
|---|---|
| 高可靠性需求(如无人值守监控页) | Web Worker + lastModified 差值检测(本文方案) |
| 轻量级场景(普通用户脚本) |
@updateURL + 页面加载时比对 GM_info.script.version,仅首次加载检测 |
| 需强制立即生效的热更新 | 改用 @require 引入外部 JS 模块,配合服务端 Cache-Control: no-cache 实现动态加载 |
通过将时间敏感逻辑移至 Web Worker,你彻底绕开了浏览器对后台标签页的节流枷锁——这才是 Tampermonkey 自动更新场景下真正可控、可预测、可维护的技术正解。

















