Update Flow
Service worker update flow ใน SvelteKit
หัวข้อที่มีชื่อว่า “Service worker update flow ใน SvelteKit”การ deploy build ใหม่หมายความว่าไฟล์ service worker ของคุณเปลี่ยนแปลง — อย่างน้อยที่สุด version ใน $service-worker จะเปลี่ยน ทำให้ `cache-\${version}` กลายเป็น string ใหม่ Browser ตรวจจับสิ่งนี้, ดาวน์โหลด SW ใหม่, และเข้าสู่ update lifecycle ที่อธิบายด้านล่าง
การ register
หัวข้อที่มีชื่อว่า “การ register”SW ในตัว (src/service-worker.js): SvelteKit ลงทะเบียนโดยอัตโนมัติระหว่าง build ใน development type จะเป็น 'module'; ใน production จะเป็น 'classic' คุณไม่จำเป็นต้องเรียก navigator.serviceWorker.register() เอง
@vite-pwa/sveltekit: Plugin จัดการ การ register ผ่านตัวเลือก registerType Module virtual:pwa-register (และ variant เฉพาะ Svelte virtual:pwa-register/svelte) เปิดเผย Svelte store ที่คุณใช้ใน component ใดก็ได้
Timeline ของการอัปเดต
หัวข้อที่มีชื่อว่า “Timeline ของการอัปเดต”sequenceDiagram participant B as Browser participant OSW as Old SW (active) participant NSW as New SW (waiting) participant P as Page B->>NSW: Download + parse new sw.js NSW->>NSW: install event (precache new assets) NSW-->>OSW: waiting — old SW still controls tabs P->>P: updatefound event fires note over P: show reload banner P->>NSW: postMessage SKIP_WAITING NSW->>NSW: skipWaiting() — activates immediately NSW->>B: clients.claim() — takes over open tabs P->>P: reload
ตัวเลือก A: autoUpdate กับ @vite-pwa/sveltekit
หัวข้อที่มีชื่อว่า “ตัวเลือก A: autoUpdate กับ @vite-pwa/sveltekit”SvelteKitPWA({ registerType: 'autoUpdate',})SW ใหม่ activate ทันทีที่ขั้นตอน install เสร็จสมบูรณ์ — ไม่มี prompt ดีที่สุดสำหรับแอปที่ URL ของ asset มี fingerprint (hash) เพื่อให้ asset เก่าและใหม่ไม่ชนกัน
ตัวเลือก B: prompt กับ virtual:pwa-register/svelte
หัวข้อที่มีชื่อว่า “ตัวเลือก B: prompt กับ virtual:pwa-register/svelte”SvelteKitPWA({ registerType: 'prompt',})SW ใหม่รอ Component Svelte ของคุณใช้ useRegisterSW จาก virtual:pwa-register/svelte เพื่อแสดง banner เมื่อมีการอัปเดตพร้อมใช้งาน:
<script lang="ts"> import { useRegisterSW } from 'virtual:pwa-register/svelte';
const { needRefresh, offlineReady, updateServiceWorker } = useRegisterSW({ onRegistered(r) { console.log('SW registered:', r); }, onRegisterError(error) { console.error('SW registration error', error); }, });
function close() { offlineReady.set(false); needRefresh.set(false); }
$: toast = $offlineReady || $needRefresh;</script>
{#if toast} <div class="pwa-toast" role="alert"> {#if $offlineReady} <span>App ready to work offline.</span> {:else} <span>New version available.</span> {/if} {#if $needRefresh} <button on:click={() => updateServiceWorker(true)}>Reload</button> {/if} <button on:click={close}>Close</button> </div>{/if}updateServiceWorker(true) ส่ง SKIP_WAITING ไปยัง SW ที่รออยู่ และ reload หน้าเมื่อ SW ตัวนั้น activate
ตัวเลือก C: manual updatefound / statechange (SW ในตัว)
หัวข้อที่มีชื่อว่า “ตัวเลือก C: manual updatefound / statechange (SW ในตัว)”เมื่อคุณใช้ src/service-worker.js โดยไม่มี @vite-pwa/sveltekit ให้เชื่อมต่อการตรวจจับการอัปเดตเอง:
// src/app.ts (หรือ module ใดก็ตามที่รันในทุกหน้า)async function registerAndWatch() { if (!('serviceWorker' in navigator)) return;
const registration = await navigator.serviceWorker.register( '/service-worker.js', { type: import.meta.env.DEV ? 'module' : 'classic' } );
registration.addEventListener('updatefound', () => { const newWorker = registration.installing; if (!newWorker) return;
newWorker.addEventListener('statechange', () => { if (newWorker.state === 'installed' && navigator.serviceWorker.controller) { showUpdateBanner(registration); } }); });}
function showUpdateBanner(registration) { const banner = document.createElement('div'); banner.textContent = 'New version available. '; const btn = document.createElement('button'); btn.textContent = 'Reload'; btn.onclick = () => { registration.waiting?.postMessage({ type: 'SKIP_WAITING' }); window.location.reload(); }; banner.appendChild(btn); document.body.prepend(banner);}
registerAndWatch();Service worker ของคุณต้องรับฟัง message นั้น:
self.addEventListener('message', (event) => { if (event.data?.type === 'SKIP_WAITING') { self.skipWaiting(); }});การตรวจสอบการอัปเดตแบบ periodic สำหรับ SPA
หัวข้อที่มีชื่อว่า “การตรวจสอบการอัปเดตแบบ periodic สำหรับ SPA”แอป SvelteKit มักเป็น SPA — ผู้ใช้อาจอยู่ในหน้าเดิมหลายชั่วโมง ให้เรียก manual update check ทุกระยะเวลา:
registration.addEventListener('updatefound', () => { /* … */ });setInterval(() => registration.update(), 60 * 60 * 1000); // ทุกชั่วโมงข้อแลกเปลี่ยน
หัวข้อที่มีชื่อว่า “ข้อแลกเปลี่ยน”| ตัวเลือก | Benefit | Cost |
|---|---|---|
ให้ SvelteKit reactive statement ($:) ตรวจ update state จาก store | UI อัปเดตทันทีแบบ declarative ไม่ต้อง manual DOM update | ต้องเข้าใจ reactivity model ของ Svelte ให้ดีไม่งั้น trigger ผิดจังหวะ |
Poll registration.update() เป็นระยะด้วยมือ | ควบคุม timing การเช็ค update ได้ชัดเจน | เพิ่ม network request โดยไม่จำเป็นถ้า interval ตั้งถี่เกินไป |
ข้อผิดพลาดที่พบบ่อย
หัวข้อที่มีชื่อว่า “ข้อผิดพลาดที่พบบ่อย”- ไม่ handle
updatefoundevent ให้ครบ ทำให้ UI ไม่รู้เลยว่ามี service worker เวอร์ชันใหม่กำลังติดตั้งอยู่เบื้องหลัง - Reload หน้าทันทีที่ state เปลี่ยนโดยไม่ถามผู้ใช้ก่อน ทำให้งานที่กำลังกรอกอยู่ในฟอร์มหายไปกลางคัน
- ไม่ debounce การเช็ค update ทำให้ยิง
registration.update()ถี่เกินจำเป็นทุกครั้งที่ navigate ไปมาในแอป
💡 ตัวอย่างจากของจริง
Vite PWA ecosystem (SvelteKit) — ใช้
virtual:pwa-register/sveltestore แจ้ง needRefresh ให้ UI แสดง toast ตามมาตรฐานเดียวกับฝั่ง React/VueElk (Mastodon client) — แสดง prompt ให้ผู้ใช้กด reload เองเมื่อมีเวอร์ชันใหม่ แทนที่จะ force reload ระหว่างกำลังเลื่อนอ่าน timeline