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

Service Worker自身加载时fetch事件未触发的原因及预获取方法问询

问题分析与解决方案

首先,你遇到的这个情况不是Bug,而是浏览器针对Service Worker的内置行为限制:当浏览器自动检查Service Worker更新(或者你主动调用registration.update())时,发起的sw.js请求会绕过当前激活的Service Worker,不会触发它的fetch事件。这么设计的核心原因是避免安全风险——防止旧的Service Worker篡改或拦截新的Service Worker脚本,确保浏览器能获取到真实的最新版本。

为什么之前可能可行?

有可能是之前的测试场景不同:比如你手动在页面里调用fetch('/sw.js')(就像你HTML代码里的runFetch函数),这种页面发起的请求会被Service Worker的fetch事件捕获,但浏览器自动触发的Service Worker更新请求是走的特殊通道,不会经过当前SW的拦截逻辑。


实现需求的可行方案

你的核心需求是“在新Service Worker加载前/时,让旧SW能获取到它的内容”,可以通过以下两种方式实现:

方案1:在页面端监听更新事件,传递新SW内容给旧SW

浏览器的Service Worker注册对象提供了updatefound事件,当发现有新的SW版本时,你可以在页面JS中获取新SW的脚本内容,然后通过postMessage发送给当前激活的旧SW。

修改你的HTML代码如下:

<html>
<body>
<h1>hello</h1>
<script src="test.js"></script>
<button onclick="runFetch()">fetch</button>
<h2 id="swVersion"></h2>
<script>
async function registerSW() {
  const registration = await navigator.serviceWorker.register('/sw.js');
  
  // 监听SW更新事件
  registration.addEventListener('updatefound', () => {
    const newSW = registration.installing;
    // 当新SW下载完成后,获取其脚本内容
    newSW.addEventListener('statechange', async () => {
      if (newSW.state === 'installed') {
        // 主动请求最新的sw.js(确保不缓存)
        const response = await fetch('/sw.js', { cache: 'no-store' });
        const newSWContent = await response.text();
        // 获取当前激活的旧SW
        const activeSW = registration.active;
        if (activeSW) {
          activeSW.postMessage({
            type: 'NEW_SW_CONTENT',
            content: newSWContent,
            version: extractVersion(newSWContent) // 自己实现提取版本的函数
          });
        }
      }
    });
  });

  // 主动触发一次更新检查
  await registration.update();
}

// 从SW脚本中提取版本号的辅助函数
function extractVersion(swContent) {
  const match = swContent.match(/const version = (\d+);/);
  return match ? parseInt(match[1]) : null;
}

registerSW();

async function runFetch() {
  console.log(await fetch('/sw.js'));
}
</script>
</body>
</html>

然后在你的Service Worker中添加message事件监听,接收页面传递的新SW内容:

const version = 1;
console.log(`SW with v${version} executed`);

async function onFetch(event) {
  console.log(`fetching: ${event.request.url} with v${version}`);
  return await fetch(event.request);
}

async function onInstall() {
  console.log(`sw v${version} install`);
  await self.skipWaiting();
}

async function onActivate() {
  console.log(`sw v${version} activate`);
  await self.clients.claim();
}

// 新增监听页面发来的消息
async function onMessage(event) {
  if (event.data.type === 'NEW_SW_CONTENT') {
    console.log(`Old SW v${version} received new SW content, version: ${event.data.version}`);
    // 这里可以处理新SW的内容,比如检查版本或特定逻辑
    // console.log('New SW content:', event.data.content);
  }
}

self.addEventListener('fetch', event => event.respondWith(onFetch(event)));
self.addEventListener('install', event => event.waitUntil(onInstall()));
self.addEventListener('activate', event => event.waitUntil(onActivate()));
self.addEventListener('message', event => onMessage(event));

方案2:旧SW主动定时检查新SW版本

如果不需要严格在“加载前”获取,也可以让旧SW在激活后,定时发起对sw.js的请求(带上cache: 'no-store'确保拿到最新内容),自行检查版本变化:

async function checkForNewSW() {
  try {
    const response = await fetch('/sw.js', { cache: 'no-store' });
    const content = await response.text();
    const match = content.match(/const version = (\d+);/);
    if (match) {
      const newVersion = parseInt(match[1]);
      if (newVersion > version) {
        console.log(`Old SW v${version} detected new version v${newVersion}`);
        // 这里可以执行你的检查逻辑
      }
    }
  } catch (err) {
    console.error('Failed to check for new SW:', err);
  }
}

// 在activate事件中启动定时检查
async function onActivate() {
  console.log(`sw v${version} activate`);
  await self.clients.claim();
  // 每5分钟检查一次
  setInterval(checkForNewSW, 5 * 60 * 1000);
}

关键注意事项

  • 浏览器自动的SW更新请求永远不会经过当前SW的fetch事件,这是硬限制,无法绕过。
  • 无论哪种方案,获取新SW内容时一定要添加cache: 'no-store',避免拿到缓存的旧版本。
  • 如果你只是需要检查版本号,也可以把版本号放在单独的version.json文件里,让SW和页面都去请求这个文件,这样更轻量,不需要解析整个SW脚本。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.07 11:57:54