Skip to content

Service Worker 与 PWA

概述

Service Worker 是 PWA 的核心,提供离线缓存、推送通知等能力。

一、生命周期

注册 → 安装 (install) → 激活 (activate) → 运行/空闲
javascript
// 注册
if ('serviceWorker' in navigator) {
  navigator.serviceWorker.register('/sw.js')
    .then(reg => console.log('Registered'))
    .catch(err => console.error(err));
}

// sw.js
self.addEventListener('install', (e) => {
  e.waitUntil(
    caches.open('v1').then(cache => {
      return cache.addAll(['/index.html', '/styles.css', '/app.js']);
    })
  );
  self.skipWaiting(); // 立即激活
});

self.addEventListener('activate', (e) => {
  e.waitUntil(
    caches.keys().then(keys => {
      return Promise.all(
        keys.filter(k => k !== 'v1').map(k => caches.delete(k))
      );
    })
  );
  self.clients.claim(); // 接管页面
});

二、缓存策略

Cache First

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

Network First

javascript
self.addEventListener('fetch', (e) => {
  e.respondWith(
    fetch(e.request).catch(() => caches.match(e.request))
  );
});

Stale-While-Revalidate

javascript
self.addEventListener('fetch', (e) => {
  e.respondWith(
    caches.open('v1').then(cache => {
      return cache.match(e.request).then(cached => {
        const fetching = fetch(e.request).then(response => {
          cache.put(e.request, response.clone());
          return response;
        });
        return cached || fetching;
      });
    })
  );
});

三、PWA Manifest

json
{
  "name": "My App",
  "short_name": "App",
  "start_url": "/",
  "display": "standalone",
  "background_color": "#ffffff",
  "theme_color": "#000000",
  "icons": [
    { "src": "/icon-192.png", "sizes": "192x192", "type": "image/png" },
    { "src": "/icon-512.png", "sizes": "512x512", "type": "image/png" }
  ]
}
html
<link rel="manifest" href="/manifest.json">

四、推送通知

javascript
// 订阅
const reg = await navigator.serviceWorker.ready;
const subscription = await reg.pushManager.subscribe({
  userVisibleOnly: true,
  applicationServerKey: vapidPublicKey
});

// sw.js
self.addEventListener('push', (e) => {
  const data = e.data.json();
  e.waitUntil(
    self.registration.showNotification(data.title, {
      body: data.body,
      icon: '/icon.png'
    })
  );
});

面试高频题

Q1: Service Worker 的作用域?

默认是 sw.js 所在目录及子目录,可通过 scope 选项限制。

Q2: 更新 Service Worker 的策略?

新版本 install 后等待旧版本卸载,或使用 skipWaiting 立即激活。

Q3: PWA 安装条件?

HTTPS、有效的 manifest、注册的 Service Worker、满足安装提示条件。

前端面试知识库