如何仅从缓存加载Web Worker,避免蜂窝网络下载Stockfish引擎?
你现有代码里有个致命错误:SetCached里检测的URL是//www.linguashop.com/course/java/stockfish-16.1-single.js,但LoadEngine里加载的是//www.mysite.com/stockfish-16.1-single.js——两个完全不同的地址,检测的缓存和实际要加载的资源根本不沾边,这肯定会导致判断失效,实际触发下载。先把这个URL统一再说。
解决了URL问题后,还要注意:Web Worker的构造函数本身不支持直接指定cache-only这类缓存策略,它的请求遵循浏览器默认的HTTP缓存规则,哪怕你用HEAD请求确认过缓存存在,Worker加载时也可能因为缓存过期、浏览器缓存空间不足等原因重新发起网络请求。
下面给你两个可靠的方案:
方案一:通过Fetch+Blob创建Worker(推荐)
先使用fetch以only-if-cached模式获取引擎文件,确认从缓存拿到后,把资源转为Blob URL,再用这个URL创建Worker。这样能彻底确保资源来自缓存,不会触发网络下载:
let stockfish = null; async function LoadEngine() { // 先判断网络类型 if (navigator.connection?.type === 'cellular') { try { // 以only-if-cached模式获取资源 const response = await fetch('//www.mysite.com/stockfish-16.1-single.js', { method: 'GET', cache: 'only-if-cached', mode: 'same-origin' }); if (!response.ok) throw new Error('资源不在缓存中'); // 转为Blob URL const blob = await response.blob(); const blobUrl = URL.createObjectURL(blob); // 创建Worker stockfish = new Worker(blobUrl); // 用完记得释放Blob URL(可选,页面卸载时处理也行) stockfish.addEventListener('error', () => URL.revokeObjectURL(blobUrl)); stockfish.addEventListener('message', () => { // 业务逻辑 }); } catch (err) { // 缓存中没有,蜂窝网络下不加载 console.log('蜂窝网络下未缓存,不加载引擎'); return; } } else { // WiFi/以太网下正常加载(可缓存) stockfish = new Worker('//www.mysite.com/stockfish-16.1-single.js'); } }
这个方案的核心是直接用缓存中的资源生成Worker的URL,完全绕开了Worker自身的请求逻辑,从根源上杜绝了网络下载。
方案二:用Service Worker拦截请求
如果你需要全局控制这类资源的加载,可以注册Service Worker,拦截所有Worker的请求,强制走缓存,禁止网络请求:
- 注册Service Worker(在主页面代码中):
if ('serviceWorker' in navigator) { navigator.serviceWorker.register('/sw.js') .then(reg => console.log('Service Worker注册成功')) .catch(err => console.log('Service Worker注册失败', err)); }
- 编写sw.js(Service Worker脚本):
self.addEventListener('fetch', (event) => { // 匹配Stockfish引擎的URL if (event.request.url.includes('stockfish-16.1-single.js')) { // 判断当前网络是否为蜂窝网络 if (navigator.connection?.type === 'cellular') { // 仅从缓存获取,找不到就返回错误 event.respondWith(caches.match(event.request)); } else { // WiFi下正常缓存策略:先读缓存,再更新缓存 event.respondWith( caches.match(event.request).then(cachedResponse => { const fetchPromise = fetch(event.request).then(networkResponse => { caches.open('stockfish-cache').then(cache => { cache.put(event.request, networkResponse.clone()); }); return networkResponse; }); return cachedResponse || fetchPromise; }) ); } } });
这个方案适合需要批量控制资源加载的场景,但需要处理Service Worker的注册、缓存更新等细节。
额外提示:navigator.connection.type在部分浏览器中可能有不同的取值(比如4g、3g也属于蜂窝网络),建议用navigator.connection.effectiveType做更精准的判断,比如effectiveType.startsWith('4g')或effectiveType.startsWith('3g')都视为蜂窝网络。
内容的提问来源于stack exchange,提问作者Cymro

