Service Worker从入门到精通我的实战踩坑经验分享

上官妍妍 优化 阅读 1,497
赞 70 收藏
二维码
手机扫码查看
反馈

Service Worker搞不定离线缓存,我差点抓狂

前几天在项目里加了个Service Worker来搞离线缓存,结果发现页面加载不出来,缓存也没生效。折腾了半天发现原来是注册Service Worker的方式不对,还有一些细节没处理好。

先说解决方案,再聊聊为什么

最终的解决方法是这样的:

// 注册Service Worker
if ('serviceWorker' in navigator) {
  window.addEventListener('load', () => {
    navigator.serviceWorker.register('/sw.js')
      .then(registration => {
        console.log('Service Worker registered with scope:', registration.scope);
      })
      .catch(error => {
        console.error('Service Worker registration failed:', error);
      });
  });
}

这里我踩了个坑,一开始我把Service Worker的注册放在了DOMContentLoaded事件里,结果页面加载不出来。后来试了下发现,应该在window的load事件里注册,这样才能确保所有的资源都加载完毕。

缓存策略也得搞清楚

接下来是缓存策略的问题。我最初写的代码是这样的:

self.addEventListener('fetch', event => {
  event.respondWith(
    caches.match(event.request)
      .then(response => {
        return response || fetch(event.request);
      })
  );
});

这个策略看起来挺简单的,就是先从缓存里找,找不到就去网络上拉。但问题是这样会导致一些动态资源(比如API请求)也被缓存下来,导致数据不更新。后来改成了这样:

const CACHE_NAME = 'my-cache-v1';
const urlsToCache = [
  '/',
  '/index.html',
  '/styles.css',
  '/script.js'
];

self.addEventListener('install', event => {
  event.waitUntil(
    caches.open(CACHE_NAME)
      .then(cache => {
        return cache.addAll(urlsToCache);
      })
  );
});

self.addEventListener('fetch', event => {
  event.respondWith(
    caches.match(event.request)
      .then(response => {
        if (response) {
          return response;
        }
        return fetch(event.request).then(networkResponse => {
          if (networkResponse && networkResponse.ok) {
            const cacheCopy = networkResponse.clone();
            caches.open(CACHE_NAME).then(cache => {
              cache.put(event.request, cacheCopy);
            });
          }
          return networkResponse;
        });
      })
  );
});

这样就能确保静态资源被缓存下来,而动态资源每次都去网络上拉取。这个方案虽然复杂一点,但效果更好。

还有一个小问题

解决了上面的问题后,又遇到了一个奇葩情况:有些页面加载出来的样式不对。查了半天发现是CSS文件的版本号没更新,浏览器还在用旧的缓存。最后在URL后面加了个版本号参数解决了这个问题:

const urlsToCache = [
  '/',
  '/index.html?v=1.0',
  '/styles.css?v=1.0',
  '/script.js?v=1.0'
];

虽然有点土,但确实有效。

总结一下

以上是我踩坑后的总结,希望能对你有帮助。如果你有更好的方案或者遇到过类似的问题,欢迎评论区交流。这个技巧的拓展用法还有很多,后续会继续分享这类博客。

本文章不代表JZTHEME立场,仅为作者个人观点 / 研究心得 / 经验分享,旨在交流探讨,供读者参考。
发表评论
设计师丽君
读完这篇文章,我对自己的知识体系有了新的认识,找到了需要补充的短板。
点赞 7
2026-01-30 23:25