Next.js应用中Service Worker动态缓存异常问题排查
Next.js Service Worker 动态缓存异常修复方案
一、未缓存页面离线仍可加载的问题分析与修复
问题原因
- 请求过滤缺失:未排除API请求、POST请求、第三方资源等不应缓存的请求,导致这类资源被误加入动态缓存;
- Fallback路径错误:使用相对路径
../fallback.js,Service Worker作用域为根目录/,public下资源需用绝对路径/fallback.js; - Next.js资源匹配逻辑疏漏:Next.js的内部路由、页面数据请求未做针对性处理,导致非预期请求被缓存。
修复措施
- 新增请求过滤逻辑,仅缓存同源GET请求;
- 修正fallback资源路径为绝对路径;
- 明确排除API、第三方资源等无需缓存的请求类型。
二、limitCache函数未生效的问题分析与修复
问题原因
- 异步逻辑未闭环:函数内部嵌套
then但未返回Promise,缓存清理操作无法被waitUntil追踪,可能提前终止; - 缓存项排序错误:
cache.keys()返回的请求顺序不代表缓存时间顺序,直接删除第一个无法保证清理最旧资源; - 时机不匹配:缓存写入未完成就触发清理,导致缓存数量判断不准确。
修复措施
- 将
limitCache改为异步函数,返回Promise确保操作可追踪; - 按缓存资源的
last-modified时间排序,优先删除最旧缓存; - 等待缓存写入完成后再执行清理逻辑。
修改后的完整sw.js代码
const staticCache = 'site-static-v1'; const dynamicCache = 'site-dy-v1'; const assets = [ '/', '/fallback.js', '/styles/globals.css', // 适配Next.js核心静态资源,可根据项目调整 '/_next/static/css/*.css', '/_next/static/js/*.js' ]; // 安装事件:缓存静态资源 self.addEventListener('install', (e) => { e.waitUntil( caches.open(staticCache) .then(cache => { console.log("缓存静态资源"); return cache.addAll(assets); }) .catch(err => console.error("静态资源缓存失败:", err)) ); }); // 激活事件:清理旧版本缓存 self.addEventListener('activate', (e) => { e.waitUntil( caches.keys().then(keys => { return Promise.all( keys.filter(key => key !== staticCache && key !== dynamicCache) .map(key => caches.delete(key)) ); }) ); }); // 限制缓存大小:异步函数,保证操作可追踪 const limitCacheSize = async (name, size) => { const cache = await caches.open(name); const keys = await cache.keys(); // 按缓存时间排序,最旧的资源排在前面 const sortedKeys = await Promise.all( keys.map(async key => { const response = await cache.match(key); return { key, timestamp: response.headers.get('last-modified') || Date.now() }; }) ).then(items => items.sort((a, b) => new Date(a.timestamp) - new Date(b.timestamp))); if (sortedKeys.length > size) { await cache.delete(sortedKeys[0].key); return limitCacheSize(name, size); } }; // Fetch事件:处理请求缓存逻辑 self.addEventListener('fetch', (e) => { // 过滤不需要缓存的请求:POST、API、第三方资源 if (e.request.method !== 'GET' || e.request.url.includes('/api/') || !e.request.url.startsWith(self.location.origin)) { return e.respondWith(fetch(e.request)); } e.respondWith( caches.match(e.request) .then(cacheRes => { // 优先返回缓存,无缓存则请求网络并缓存 return cacheRes || fetch(e.request) .then(fetchRes => { return caches.open(dynamicCache) .then(cache => { return cache.put(e.request, fetchRes.clone()) .then(() => limitCacheSize(dynamicCache, 6)) .then(() => fetchRes); }); }) }) .catch(() => caches.match('/fallback.js')) ); });
额外注意事项
- Next.js页面数据缓存:若需缓存页面数据,需添加
/_next/data/[version]/[path].json这类路径的匹配规则; - Service Worker注册:在Next.js中需手动注册SW,示例代码(Pages Router在
_app.js,App Router在layout.js):useEffect(() => { if ('serviceWorker' in navigator) { navigator.serviceWorker.register('/sw.js') .then(reg => console.log('SW注册成功:', reg)) .catch(err => console.error('SW注册失败:', err)); } }, []); - 缓存版本更新:修改缓存名称(如
site-static-v2)可触发SW重新安装,自动清理旧缓存。
内容的提问来源于stack exchange,提问作者Atharv Arolkar
相关产品推荐
相关产品推荐

