ข้ามไปเนื้อหาหลัก

Category: guide

Service Workers — Cache Strategy สำหรับ Static Site

คู่มือ Service Worker สำหรับ offline caching, stale-while-revalidate และ cache management บน static site

· อ่านประมาณ 1 นาที

สารบัญ

Service Worker คืออะไร

Service Worker เป็น JavaScript ที่รันใน background thread แยกจาก page — intercept network requests และ serve จาก cache ได้ ทำให้ site ทำงาน offline และโหลดเร็วขึ้นมาก

Register

// main.js
if ('serviceWorker' in navigator) {
  navigator.serviceWorker.register('/sw.js');
}

Cache Strategies

Cache First (static assets)

// sw.js
self.addEventListener('fetch', (event) => {
  event.respondWith(
    caches.match(event.request).then((cached) =>
      cached ?? fetch(event.request).then((res) => {
        const clone = res.clone();
        caches.open('v1').then((cache) => cache.put(event.request, clone));
        return res;
      })
    )
  );
});

Stale-While-Revalidate (HTML pages)

self.addEventListener('fetch', (event) => {
  event.respondWith(
    caches.open('pages').then(async (cache) => {
      const cached = await cache.match(event.request);
      const fetchPromise = fetch(event.request).then((res) => {
        cache.put(event.request, res.clone());
        return res;
      });
      return cached ?? fetchPromise;
    })
  );
});

Install & Activate

const CACHE_NAME = 'site-v2';
const PRECACHE = ['/', '/offline.html', '/styles.css'];

self.addEventListener('install', (e) => {
  e.waitUntil(caches.open(CACHE_NAME).then((c) => c.addAll(PRECACHE)));
  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();
});

Workbox — ทำให้ง่ายขึ้นมาก

npm install workbox-window
// sw.js (generated by Workbox CLI)
import { precacheAndRoute } from 'workbox-precaching';
import { registerRoute } from 'workbox-routing';
import { StaleWhileRevalidate } from 'workbox-strategies';

precacheAndRoute(self.__WB_MANIFEST);  // inject during build

registerRoute(
  ({ request }) => request.destination === 'document',
  new StaleWhileRevalidate({ cacheName: 'pages' })
);