Service Worker 与离线存储

深入理解 Service Worker 的生命周期、缓存策略设计,以及 PWA 离线应用的实现原理。


Service Worker 是什么

Service Worker 是浏览器在后台运行的脚本,独立于网页。它是浏览器和网络之间的代理,可以:

  • 拦截网络请求
  • 缓存资源
  • 推送通知
  • 后台同步

与其他 Worker 的区别

  • Web Worker:在后台线程运行脚本,不阻塞主线程
  • Service Worker:在浏览器幕后运行,可以拦截网络请求

Service Worker 的生命周期

Installing → Installed → Activating → Activated → Redundant

1. Installing

// sw.js
const CACHE_NAME = 'v1';

self.addEventListener('install', event => {
  console.log('SW Installing');
  // 缓存静态资源
  event.waitUntil(
    caches.open(CACHE_NAME).then(cache => {
      return cache.addAll([
        '/',
        '/styles.css',
        '/script.js'
      ]);
    })
  );
});

2. Activating

self.addEventListener('activate', event => {
  console.log('SW Activating');
  // 清理旧缓存
  event.waitUntil(
    caches.keys().then(cacheNames => {
      return Promise.all(
        cacheNames
          .filter(name => name !== CACHE_NAME)
          .map(name => caches.delete(name))
      );
    })
  );
});

3. Fetch

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

常用缓存策略

策略一:Cache First(缓存优先)

适合静态资源(CSS、JS、图片):

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

策略二:Network First(网络优先)

适合 API 数据:

self.addEventListener('fetch', event => {
  event.respondWith(
    fetch(event.request)
      .then(response => {
        // 缓存新内容
        const clone = response.clone();
        caches.open(CACHE_NAME).then(cache => {
          cache.put(event.request, clone);
        });
        return response;
      })
      .catch(() => {
        // 网络失败时用缓存
        return caches.match(event.request);
      })
  );
});

策略三:Stale While Revalidate

先返回缓存,同时在后台更新缓存:

self.addEventListener('fetch', event => {
  event.respondWith(
    caches.open(CACHE_NAME).then(cache => {
      return cache.match(event.request).then(response => {
        // 发起新请求更新缓存
        const fetchPromise = fetch(event.request).then(networkResponse => {
          cache.put(event.request, networkResponse.clone());
          return networkResponse;
        });
        // 返回缓存或等待新内容
        return response || fetchPromise;
      });
    })
  );
});

PWA 离线应用

注册 Service Worker

// main.js
if ('serviceWorker' in navigator) {
  navigator.serviceWorker.register('/sw.js')
    .then(reg => console.log('SW registered', reg))
    .catch(err => console.error('SW registration failed', err));
}

Web App Manifest

// manifest.json
{
  "name": "My PWA",
  "short_name": "MyApp",
  "start_url": "/",
  "display": "standalone",
  "background_color": "#ffffff",
  "icons": [
    { "src": "/icon-192.png", "sizes": "192x192", "type": "image/png" },
    { "src": "/icon-512.png", "sizes": "512x512", "type": "image/png" }
  ]
}

这一章想说的

Service Worker 是 PWA 的核心技术:

  1. 生命周期:Installing → Activating → Fetch
  2. 缓存策略:Cache First、Network First、Stale While Revalidate
  3. 离线能力:配合 Cache Storage 实现完整离线应用

Service Worker 让 Web 应用可以像原生应用一样离线使用、推送通知。


延展阅读