Progressive Web App使用Service Worker实现资源分组渐进式缓存方法问询
实现方案
核心逻辑是通过 postMessage 实现主线程和 Service Worker 之间的通信,主线程发送预缓存指令,Service Worker 收到后执行对应关卡资源的缓存逻辑。
第一步:扩展 Service Worker 代码,新增消息监听
在原有 Service Worker 代码基础上,新增消息事件监听,处理预缓存请求:
// 原有代码保留 self.addEventListener("fetch", event => { event.respondWith( caches.match(event.request) .then( cachedResponse => { return cachedResponse || fetch(event.request); } ) ); }); const cacheName0 = "bare-bones-of-the-app"; const mainResourcesToPrecache = [ "index.html", "app.js", "style.css" ]; self.addEventListener("install", event => { event.waitUntil( caches.open(cacheName0).then(cache => { return cache.addAll(mainResourcesToPrecache); }) ); }); // ========== 新增代码开始 ========== // 监听主线程发来的消息 self.addEventListener('message', event => { // 只处理预缓存关卡的指令 if (event.data?.type === 'PRECACHE_LEVEL') { const { levelId, resources } = event.data.payload; const cacheName = `level-${levelId}`; // 等待缓存逻辑执行完成 event.waitUntil( caches.open(cacheName) .then(cache => cache.addAll(resources)) .then(() => { // 缓存成功后给主线程回传通知 event.source.postMessage({ type: 'PRECACHE_SUCCESS', payload: { levelId } }); }) .catch(err => { // 缓存失败回传错误信息 event.source.postMessage({ type: 'PRECACHE_FAILED', payload: { levelId, error: err.message } }); }) ); } }); // ========== 新增代码结束 ==========
第二步:主线程封装预缓存触发函数
在主线程代码中封装调用方法,主动向 Service Worker 发送预缓存指令:
// 主线程预缓存触发函数 async function precacheLevelResources(levelId, resources) { // 等待Service Worker激活完成 const registration = await navigator.serviceWorker.ready; const activeSW = registration.active; if (!activeSW) { throw new Error('Service Worker 尚未激活,无法预缓存资源'); } // 异步等待Service Worker返回处理结果 return new Promise((resolve, reject) => { const messageHandler = (event) => { // 匹配当前关卡的处理结果 if (event.data?.payload?.levelId !== levelId) return; if (event.data.type === 'PRECACHE_SUCCESS') { navigator.serviceWorker.removeEventListener('message', messageHandler); resolve(); } if (event.data.type === 'PRECACHE_FAILED') { navigator.serviceWorker.removeEventListener('message', messageHandler); reject(new Error(event.data.payload.error)); } }; navigator.serviceWorker.addEventListener('message', messageHandler); // 发送预缓存指令 activeSW.postMessage({ type: 'PRECACHE_LEVEL', payload: { levelId, resources } }); }); }
第三步:业务场景调用
可以根据游戏进度选择合适的触发时机,比如用户通关当前关卡时,提前加载下一关资源:
// 示例:用户通关第1关后,预加载第2关资源 const resourcesForLevel2 = [ "anotherpicture.jpg", "anotherartwork.png", "anothersound.mp3" ]; precacheLevelResources(2, resourcesForLevel2) .then(() => { console.log('第2关资源预加载完成,可直接进入'); }) .catch(err => { console.error('第2关资源预加载失败:', err); // 可在这里添加重试逻辑 });
补充说明
- 触发时机可灵活调整,比如用户当前关卡进度达到80%时就提前触发预加载,进一步减少用户等待时间
- 如果关卡资源体积较大,可拆分分批加载,避免占用过多带宽影响当前关卡的正常运行
- 不需要的旧关卡资源可主动触发删除,节省用户设备存储空间
内容的提问来源于stack exchange,提问作者BulliedByModeratorsAgain_Again
相关产品推荐
相关产品推荐

