ข้ามไปยังเนื้อหา

Update Flow

การ deploy build ใหม่หมายความว่าไฟล์ service worker ของคุณเปลี่ยนแปลง — อย่างน้อยที่สุด version ใน $service-worker จะเปลี่ยน ทำให้ `cache-\${version}` กลายเป็น string ใหม่ Browser ตรวจจับสิ่งนี้, ดาวน์โหลด SW ใหม่, และเข้าสู่ update lifecycle ที่อธิบายด้านล่าง

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 ใดก็ได้

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
Service worker update sequence
vite.config.ts
SvelteKitPWA({
registerType: 'autoUpdate',
})

SW ใหม่ activate ทันทีที่ขั้นตอน install เสร็จสมบูรณ์ — ไม่มี prompt ดีที่สุดสำหรับแอปที่ URL ของ asset มี fingerprint (hash) เพื่อให้ asset เก่าและใหม่ไม่ชนกัน

vite.config.ts
SvelteKitPWA({
registerType: 'prompt',
})

SW ใหม่รอ Component Svelte ของคุณใช้ useRegisterSW จาก virtual:pwa-register/svelte เพื่อแสดง banner เมื่อมีการอัปเดตพร้อมใช้งาน:

src/lib/ReloadPrompt.svelte
<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

เมื่อคุณใช้ 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 นั้น:

src/service-worker.js
self.addEventListener('message', (event) => {
if (event.data?.type === 'SKIP_WAITING') {
self.skipWaiting();
}
});

แอป SvelteKit มักเป็น SPA — ผู้ใช้อาจอยู่ในหน้าเดิมหลายชั่วโมง ให้เรียก manual update check ทุกระยะเวลา:

registration.addEventListener('updatefound', () => { /* … */ });
setInterval(() => registration.update(), 60 * 60 * 1000); // ทุกชั่วโมง
ตัวเลือกBenefitCost
ให้ SvelteKit reactive statement ($:) ตรวจ update state จาก storeUI อัปเดตทันทีแบบ declarative ไม่ต้อง manual DOM updateต้องเข้าใจ reactivity model ของ Svelte ให้ดีไม่งั้น trigger ผิดจังหวะ
Poll registration.update() เป็นระยะด้วยมือควบคุม timing การเช็ค update ได้ชัดเจนเพิ่ม network request โดยไม่จำเป็นถ้า interval ตั้งถี่เกินไป
  • ไม่ handle updatefound event ให้ครบ ทำให้ UI ไม่รู้เลยว่ามี service worker เวอร์ชันใหม่กำลังติดตั้งอยู่เบื้องหลัง
  • Reload หน้าทันทีที่ state เปลี่ยนโดยไม่ถามผู้ใช้ก่อน ทำให้งานที่กำลังกรอกอยู่ในฟอร์มหายไปกลางคัน
  • ไม่ debounce การเช็ค update ทำให้ยิง registration.update() ถี่เกินจำเป็นทุกครั้งที่ navigate ไปมาในแอป

💡 ตัวอย่างจากของจริง

Vite PWA ecosystem (SvelteKit) — ใช้ virtual:pwa-register/svelte store แจ้ง needRefresh ให้ UI แสดง toast ตามมาตรฐานเดียวกับฝั่ง React/Vue

Elk (Mastodon client) — แสดง prompt ให้ผู้ใช้กด reload เองเมื่อมีเวอร์ชันใหม่ แทนที่จะ force reload ระหว่างกำลังเลื่อนอ่าน timeline

registerType: "autoUpdate" ทำอะไรเมื่อ SW ใหม่ถูกติดตั้ง?
virtual module ใดที่เปิดเผย needRefresh และ updateServiceWorker เป็น Svelte store?
ในรูปแบบ manual update หน้าส่ง message type ใดไปยัง SW ที่รออยู่?
ทำไมการให้ skipWaiting() ขึ้นอยู่กับผู้ใช้จึงสำคัญ แทนที่จะเรียกโดยไม่มีเงื่อนไขใน install?