Service Worker未重载无法提供缓存资源的问题排查
Service Worker离线缓存失效问题分析与解决
问题描述
基于MDN示例修改了Service Worker代码,硬刷新页面后缓存已填充所有指定资源,但立即设为离线重载时,多个资源无法被Service Worker获取;离线前多刷新一次,就能正常返回缓存资源。
代码展示
main.js
const registerServiceWorker = async () => { if ("serviceWorker" in navigator) { try { const registration = await navigator.serviceWorker.register("./sw.js", { scope: "./", }); } catch (error) { console.error(`Registration failed with ${error}`); } } }; registerServiceWorker();
sw.js
const CACHE_NAME = "food-journal-v1"; const ASSETS = [ "index.html", "ReviewDetails.html", "CreatePage.html", "static/CoveredByYourGrace-Regular.ttf", "static/CreatePage.css", "static/Form.css", "static/homepage.css", "static/ReviewDetails.css", "assets/images/0-star.svg", "assets/images/1-star.svg", "assets/images/2-star.svg", "assets/images/3-star.svg", "assets/images/4-star.svg", "assets/images/5-star.svg", "assets/images/default_plate.png", "assets/images/delete_icon_for_interface.png", "assets/images/edit_button_for_interface.png", "assets/images/Grouppink.png", "assets/images/home_button_for_interface.png", "assets/images/favicon.ico", "assets/images/Logo.png", "assets/scripts/CreatePage.js", "assets/scripts/localStorage.js", "assets/scripts/main.js", "assets/scripts/ReviewCard.js", "assets/scripts/ReviewDetails.js", ]; self.addEventListener("install", async () => { const cache = await caches.open(CACHE_NAME); await cache.addAll(ASSETS); }); self.addEventListener("fetch", (event) => { event.respondWith(caches.open(CACHE_NAME).then((cache) => { return fetch(event.request).then((fetchedResponse) => { cache.put(event.request, fetchedResponse.clone()); return fetchedResponse; }).catch(() => { return cache.match(event.request); }); })); });
问题成因
这是Service Worker的激活时机特性导致的:
- 硬刷新页面后,Service Worker完成安装,但默认会处于等待状态,直到当前页面的旧Service Worker实例被销毁(页面关闭或刷新)才会激活。
- 第一次离线重载时,新Service Worker还未激活,无法接管页面请求,因此无法从缓存读取资源。
- 第二次刷新时,旧Service Worker被销毁,新Service Worker激活并控制页面,此时就能正常调用缓存资源。
解决方案
修改sw.js,在install事件中添加self.skipWaiting()跳过等待,同时添加activate事件调用clients.claim()让激活后的Service Worker立即控制所有打开的页面:
const CACHE_NAME = "food-journal-v1"; const ASSETS = [ "index.html", "ReviewDetails.html", "CreatePage.html", "static/CoveredByYourGrace-Regular.ttf", "static/CreatePage.css", "static/Form.css", "static/homepage.css", "static/ReviewDetails.css", "assets/images/0-star.svg", "assets/images/1-star.svg", "assets/images/2-star.svg", "assets/images/3-star.svg", "assets/images/4-star.svg", "assets/images/5-star.svg", "assets/images/default_plate.png", "assets/images/delete_icon_for_interface.png", "assets/images/edit_button_for_interface.png", "assets/images/Grouppink.png", "assets/images/home_button_for_interface.png", "assets/images/favicon.ico", "assets/images/Logo.png", "assets/scripts/CreatePage.js", "assets/scripts/localStorage.js", "assets/scripts/main.js", "assets/scripts/ReviewCard.js", "assets/scripts/ReviewDetails.js", ]; self.addEventListener("install", async () => { const cache = await caches.open(CACHE_NAME); await cache.addAll(ASSETS); // 跳过等待阶段,直接进入激活状态 self.skipWaiting(); }); self.addEventListener("activate", (event) => { // 立即控制所有已打开的客户端页面 event.waitUntil(clients.claim()); }); self.addEventListener("fetch", (event) => { event.respondWith(caches.open(CACHE_NAME).then((cache) => { return fetch(event.request).then((fetchedResponse) => { cache.put(event.request, fetchedResponse.clone()); return fetchedResponse; }).catch(() => { return cache.match(event.request); }); })); });
修改后,硬刷新页面完成Service Worker安装后会立即激活并控制当前页面,此时切换到离线模式重载,就能直接从缓存获取资源。
内容的提问来源于stack exchange,提问作者luart
相关产品推荐
相关产品推荐

