You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Next.js应用中Service Worker动态缓存异常问题排查

Next.js Service Worker 动态缓存异常修复方案

一、未缓存页面离线仍可加载的问题分析与修复

问题原因

  1. 请求过滤缺失:未排除API请求、POST请求、第三方资源等不应缓存的请求,导致这类资源被误加入动态缓存;
  2. Fallback路径错误:使用相对路径../fallback.js,Service Worker作用域为根目录/,public下资源需用绝对路径/fallback.js;
  3. Next.js资源匹配逻辑疏漏:Next.js的内部路由、页面数据请求未做针对性处理,导致非预期请求被缓存。

修复措施

  • 新增请求过滤逻辑,仅缓存同源GET请求;
  • 修正fallback资源路径为绝对路径;
  • 明确排除API、第三方资源等无需缓存的请求类型。

二、limitCache函数未生效的问题分析与修复

问题原因

  1. 异步逻辑未闭环:函数内部嵌套then但未返回Promise,缓存清理操作无法被waitUntil追踪,可能提前终止;
  2. 缓存项排序错误:cache.keys()返回的请求顺序不代表缓存时间顺序,直接删除第一个无法保证清理最旧资源;
  3. 时机不匹配:缓存写入未完成就触发清理,导致缓存数量判断不准确。

修复措施

  • 将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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 19:35:16