如何在Violentmonkey脚本中用Service Worker拦截fetch并缓存音频?
解决方案:用户脚本中拦截Fetch并缓存音频
首先要明确:你用的普通Worker根本无法拦截页面的全局Fetch请求,它只能监听自身内部发起的Fetch,所以你的代码不会触发页面的Fetch事件,只有Service Worker才能实现全局请求拦截。下面提供两种可行方案:
方案一:用Service Worker实现拦截(推荐)
用户脚本中无法直接用相对URL注册Service Worker,核心问题在于脚本沙箱限制,需要访问页面的真实navigator对象,同时用Blob生成Service Worker脚本的URL来注册。
代码示例
// 构造Service Worker的核心逻辑 const swCode = ` self.addEventListener('fetch', (event) => { // 匹配音频文件,可根据实际需求修改规则 const audioExtensions = /\\.(mp3|wav|ogg|flac)$/i; if (audioExtensions.test(event.request.url)) { event.respondWith( caches.match(event.request) .then(cachedResponse => { // 有缓存直接返回 if (cachedResponse) { console.log('返回缓存音频:', event.request.url); return cachedResponse; } // 无缓存则请求并缓存 return fetch(event.request) .then(networkResponse => { caches.open('audio-cache-v1') .then(cache => cache.put(event.request, networkResponse.clone())); return networkResponse; }); }) ); } }); `; // 生成Service Worker的Blob URL const swBlob = new Blob([swCode], { type: 'text/javascript' }); const swUrl = URL.createObjectURL(swBlob); // 访问页面的真实navigator(Tampermonkey用unsafeWindow,Greasemonkey用GM.window) const pageNavigator = unsafeWindow.navigator; // 注册Service Worker,scope设为根目录确保覆盖所有音频请求 pageNavigator.serviceWorker.register(swUrl, { scope: '/' }) .then(reg => console.log('Service Worker注册成功:', reg.scope)) .catch(err => console.error('Service Worker注册失败:', err));
注意事项
- 脚本管理器权限:需开启对目标站点的访问权限(如Tampermonkey中设为"在所有网站上")
- HTTPS限制:Service Worker仅支持HTTPS环境(localhost除外),HTTP站点无法使用
- 缓存维护:可添加缓存清理逻辑,比如定期删除旧版本缓存
方案二:重写页面的Fetch函数(无需Service Worker)
如果目标站点是HTTP,或者无法使用Service Worker,可直接劫持页面的全局Fetch函数,在主线程实现缓存逻辑。
代码示例
// 保存原始Fetch函数 const originalFetch = unsafeWindow.fetch; // 重写全局Fetch unsafeWindow.fetch = async function(...args) { const request = new Request(args[0], args[1]); // 匹配音频文件 const audioExtensions = /\\.(mp3|wav|ogg|flac)$/i; if (audioExtensions.test(request.url)) { const cacheKey = `audio-cache:${request.url}`; // 检查localStorage缓存 const cachedData = localStorage.getItem(cacheKey); if (cachedData) { console.log('返回缓存音频:', request.url); const blob = await fetch(cachedData).then(res => res.blob()); return new Response(blob, { headers: request.headers }); } // 发起原始请求并缓存 const response = await originalFetch(...args); const blob = await response.clone().blob(); const reader = new FileReader(); reader.onload = () => localStorage.setItem(cacheKey, reader.result); reader.readAsDataURL(blob); return response; } // 非音频请求调用原始Fetch return originalFetch(...args); };
优缺点
- 优点:无需Service Worker,兼容性更好,支持HTTP站点
- 缺点:缓存存在localStorage,容量有限(约5MB);仅能拦截Fetch请求,XMLHttpRequest需额外处理;主线程处理缓存可能影响页面性能
内容的提问来源于stack exchange,提问作者SeetheMoar
相关产品推荐
相关产品推荐

