什么是 PWA?Service Worker 的作用是什么?

PWA(Progressive Web App)

渐进式 Web 应用,结合 Web 和原生应用的优点:

Service Worker

独立于主线程的后台脚本,充当浏览器与网络之间的代理。

特点:

基本使用

注册 Service Worker:

// main.js
if ('serviceWorker' in navigator) {
  window.addEventListener('load', () => {
    navigator.serviceWorker.register('/sw.js')
      .then(reg => console.log('SW registered'))
      .catch(err => console.log('SW failed:', err))
  })
}

Service Worker 生命周期:

// sw.js
const CACHE_NAME = 'v1'
const ASSETS = ['/', '/index.html', '/styles.css', '/app.js']

// 安装:预缓存资源
self.addEventListener('install', (e) => {
  e.waitUntil(
    caches.open(CACHE_NAME).then(cache => cache.addAll(ASSETS))
  )
  self.skipWaiting()  // 立即激活
})

// 激活:清理旧缓存
self.addEventListener('activate', (e) => {
  e.waitUntil(
    caches.keys().then(keys => 
      Promise.all(keys.filter(k => k !== CACHE_NAME).map(k => caches.delete(k)))
    )
  )
  self.clients.claim()
})

// 拦截请求
self.addEventListener('fetch', (e) => {
  e.respondWith(
    caches.match(e.request).then(cached => {
      return cached || fetch(e.request).then(response => {
        // 缓存新资源
        const clone = response.clone()
        caches.open(CACHE_NAME).then(cache => cache.put(e.request, clone))
        return response
      }).catch(() => caches.match('/offline.html'))
    })
  )
})

缓存策略

// 1. Cache First(适合静态资源)
async function cacheFirst(request) {
  const cached = await caches.match(request)
  return cached || fetch(request)
}

// 2. Network First(适合 API 请求)
async function networkFirst(request) {
  try {
    const response = await fetch(request)
    const clone = response.clone()
    const cache = await caches.open('api-cache')
    cache.put(request, clone)
    return response
  } catch {
    return caches.match(request)
  }
}

// 3. Stale While Revalidate(先缓存后更新)
async function staleWhileRevalidate(request) {
  const cache = await caches.open('dynamic')
  const cached = await cache.match(request)
  const fetchPromise = fetch(request).then(response => {
    cache.put(request, response.clone())
    return response
  })
  return cached || fetchPromise
}

manifest.json(可安装)

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

推送通知

// 请求权限
const permission = await Notification.requestPermission()

// 订阅推送
const reg = await navigator.serviceWorker.ready
const subscription = await reg.pushManager.subscribe({
  userVisibleOnly: true,
  applicationServerKey: VAPID_PUBLIC_KEY
})

// Service Worker 接收推送
self.addEventListener('push', (e) => {
  e.waitUntil(
    self.registration.showNotification('标题', {
      body: e.data.text(),
      icon: '/icon.png'
    })
  )
})

适用场景

同分类其他题目