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

Next.js v13+/app目录下Service Worker实现问题求助

在Next.js 13+ App Router中实现Service Worker

一、在app/page.js中注册Service Worker

App Router默认是React Server Components,所以注册逻辑必须放在客户端组件中,步骤如下:

  1. 在app/page.js顶部添加'use client'指令,将其转为客户端组件
  2. 编写注册逻辑,注意环境判断(开发环境下Next.js默认不支持Service Worker,建议只在生产环境注册)
'use client';

import { useEffect } from 'react';

export default function Home() {
  useEffect(() => {
    // 仅在生产环境且浏览器支持Service Worker时注册
    if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
      const registerSW = async () => {
        try {
          const registration = await navigator.serviceWorker.register('/service-worker.js');
          console.log('Service Worker注册成功:', registration.scope);
        } catch (error) {
          console.error('Service Worker注册失败:', error);
        }
      };

      registerSW();
    }
  }, []);

  return (
    // 你的页面内容
    <div>Hello World</div>
  );
}

二、Service Worker核心逻辑(service-worker.js)

将service-worker.js放在项目根目录的public文件夹下(Next.js会直接托管该目录下的静态文件),以下是实现缓存策略和离线支持的核心代码:

// 缓存名称及版本,更新版本号可触发缓存更新
const CACHE_NAME = 'next-app-cache-v1';
// 预缓存的关键资源(根据你的项目调整)
const PRECACHE_ASSETS = [
  '/',
  '/_next/static/css/*.css',
  '/_next/static/js/*.js',
  // 其他你想预缓存的静态资源
];

// 安装阶段:预缓存关键资源
self.addEventListener('install', (event) => {
  event.waitUntil(
    caches.open(CACHE_NAME)
      .then((cache) => cache.addAll(PRECACHE_ASSETS))
      .then(() => self.skipWaiting()) // 立即激活新的Service Worker
  );
});

// 激活阶段:清理旧缓存
self.addEventListener('activate', (event) => {
  event.waitUntil(
    caches.keys().then((cacheNames) => {
      return Promise.all(
        cacheNames.filter((name) => name !== CACHE_NAME)
          .map((name) => caches.delete(name))
      );
    }).then(() => self.clients.claim()) // 接管所有打开的客户端
  );
});

// 请求拦截:根据资源类型使用不同缓存策略
self.addEventListener('fetch', (event) => {
  // 跳过Next.js的内部API请求(如/_next/data),避免干扰动态内容
  if (event.request.url.includes('/_next/data/')) {
    return;
  }

  // 静态资源:缓存优先,离线时使用缓存
  if (event.request.mode === 'navigate' || event.request.destination === 'style' || event.request.destination === 'script' || event.request.destination === 'image') {
    event.respondWith(
      caches.match(event.request)
        .then((cachedResponse) => {
          // 同时发起网络请求更新缓存
          const fetchPromise = fetch(event.request)
            .then((networkResponse) => {
              caches.open(CACHE_NAME).then((cache) => {
                cache.put(event.request, networkResponse.clone());
              });
              return networkResponse;
            });
          // 优先返回缓存,缓存不存在则用网络请求结果
          return cachedResponse || fetchPromise;
        })
    );
  }

  // API请求:网络优先,离线时 fallback 到缓存(可选)
  if (event.request.url.includes('/api/')) {
    event.respondWith(
      fetch(event.request)
        .then((networkResponse) => {
          // 更新缓存
          caches.open(CACHE_NAME).then((cache) => {
            cache.put(event.request, networkResponse.clone());
          });
          return networkResponse;
        })
        .catch(() => caches.match(event.request)) // 离线时返回缓存的API结果
    );
  }
});

三、Next.js App Router特有的注意事项

  • 必须用客户端组件注册:App Router默认是Server Components,Service Worker的注册逻辑只能在标记了'use client'的组件中执行,否则会报错。
  • 开发环境限制:Next.js开发服务器(next dev)不支持Service Worker,必须构建生产版本(next build && next start)才能测试。
  • 静态资源路径:Next.js的静态资源都在/_next/static目录下,缓存时要包含这些路径,避免离线时样式、脚本无法加载。
  • ISR与动态内容处理:如果使用增量静态生成(ISR),Service Worker可能会缓存旧的静态页面,需要在页面更新时主动触发缓存刷新(比如通过postMessage通知Service Worker更新缓存)。
  • Service Worker文件位置:service-worker.js必须放在public目录下,否则Next.js的路由系统会拦截请求,导致无法注册。
  • HTTPS要求:生产环境必须使用HTTPS才能启用Service Worker(localhost除外),否则浏览器会阻止注册。
  • 缓存版本管理:每次更新Service Worker逻辑时,要修改CACHE_NAME的版本号,确保浏览器会安装新的Service Worker并清理旧缓存。

内容的提问来源于stack exchange,提问作者Last GAMER

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 21:33:12