Service Worker在Firebase Hosting无法返回/offline.html fallback页面求助
问题描述
Service Worker无法返回fallback页面(/offline.html),该问题仅在Firebase Hosting服务器环境中出现,本地主机下可正常返回。目前无报错,但控制台持续出现如下警告:
针对"
"的FetchEvent触发网络错误响应:重定向响应被用于重定向模式非"follow"的请求。
原代码
self.addEventListener("fetch", (evt) => { handleFetchIvent(evt); }); function handleFetchIvent(evt) { if (evt.request.redirect == "follow") { console.log(evt.request.redirect); evt.respondWith( caches .match(evt.request) .then((cacheRes) => { return ( cacheRes || fetch(evt.request.url).then( async (fetchRes) => { // got a response from server const cache = await caches.open(dynamicCacheName); cache.put(evt.request.url, fetchRes.clone()); // check cached items size limitCacheSize(dynamicCacheName, 5); return fetchRes; }, (error) => { // network error return cache.match("/offline.html"); } ) ); }) .catch(() => { return caches.match("/offline.html"); }) ); } // Change the redirect mode else if ( evt.request.redirect == "manual" || evt.request.redirect == "error" ) { const modRequest = new Request(evt.request, { redirect: "follow", }); console.log(modRequest.redirect); evt.respondWith( caches .match(modRequest) .then((cacheRes) => { return ( cacheRes || fetch(modRequest.url).then( async (fetchRes) => { // got a response from server const cache = await caches.open(dynamicCacheName); cache.put(modRequest.url, fetchRes.clone()); // check cached items size limitCacheSize(dynamicCacheName, 5); return fetchRes; }, (error) => { // network error return cache.match("/offline.html"); } ) ); }) .catch(() => { return caches.match("/offline.html"); }) ); } }
问题分析与修复
Firebase Hosting会对部分请求(如不存在的资源)返回重定向响应,原代码存在两个核心问题:
- 分支处理非"follow"重定向请求时,用修改后的请求对象匹配缓存,但缓存中存储的是原始请求,导致匹配失败直接走网络请求;
fetch仅传入URL而非完整请求对象,丢失了请求的上下文信息,在Firebase环境下易引发重定向相关的警告。
修改后的代码如下:
self.addEventListener("fetch", (evt) => { handleFetchEvent(evt); }); function handleFetchEvent(evt) { let requestToUse = evt.request; // 统一将所有请求的重定向模式设为follow,消除警告 if (evt.request.redirect !== "follow") { requestToUse = new Request(evt.request, { redirect: "follow" }); } evt.respondWith( caches.match(requestToUse) .then(cacheRes => { // 缓存命中直接返回 if (cacheRes) return cacheRes; // 缓存未命中则发起网络请求 return fetch(requestToUse) .then(async fetchRes => { // 将响应存入动态缓存 const cache = await caches.open(dynamicCacheName); cache.put(requestToUse, fetchRes.clone()); limitCacheSize(dynamicCacheName, 5); return fetchRes; }) .catch(() => { // 网络失败时返回离线页面 return caches.match("/offline.html"); }); }) .catch(() => { // 缓存匹配失败时返回离线页面 return caches.match("/offline.html"); }) ); }
关键修改点
- 简化逻辑:统一处理所有重定向模式的请求,无需分支判断;
- 保留请求上下文:
fetch使用完整请求对象而非仅URL,确保请求属性完整; - 强化失败处理:所有异常路径(网络错误、缓存匹配失败)都兜底返回离线页面。
内容的提问来源于stack exchange,提问作者Nira Mosh
相关产品推荐
相关产品推荐

