Service Worker ใน TypeScript
บทความนี้อธิบายเกี่ยวกับ Service Worker ใน TypeScript
เราจะอธิบายเกี่ยวกับ Service Worker ใน TypeScript พร้อมตัวอย่างที่ใช้งานได้จริง
YouTube Video
Service Worker ใน TypeScript
Service Worker คือ “พร็อกซีสำหรับคำขอ (request)” ที่อยู่ระหว่างเบราว์เซอร์และเครือข่าย มันสามารถดักจับ fetch ควบคุมแคช รองรับออฟไลน์ และประมวลผลเบื้องหลัง (เช่น sync และ push) การใช้ TypeScript ทำให้มีความปลอดภัยด้านชนิดข้อมูล (type safety) และเพิ่มความดูแลง่าย (maintainability)
ตั้งค่า TypeScript
tsconfig.json
(เปิดใช้งานชนิด WebWorker)
มาดูตัวอย่างการเปิดใช้ชนิด WebWorker ใน tsconfig.json
1{
2 "compilerOptions": {
3 "target": "ES2020",
4 "module": "ES2020",
5 "lib": ["ES2020", "WebWorker"],
6 "moduleResolution": "Bundler",
7 "strict": true,
8 "noEmitOnError": true,
9 "outDir": "out",
10 "skipLibCheck": true
11 },
12 "include": ["sw.ts"]
13}
- เมื่อเพิ่ม
WebWorker
ลงใน arraylib
จะสามารถใช้ชนิดต่างๆ เช่นServiceWorkerGlobalScope
ได้ DOM
และWebWorker
มีประเภทที่แตกต่างกัน ดังนั้นโดยปกติจึงแยกการตั้งค่าtsconfig.json
สำหรับเบราว์เซอร์ (แอปหลัก) และService Worker
- ไฟล์
Service Worker
จะถูกสร้างขึ้นที่ เส้นทางที่ตรงกับขอบเขต (scope) (โดยปกติคือ root ของเว็บไซต์/sw.js
) - ด้วยเหตุผลด้านความปลอดภัย Service Worker สามารถทำงานได้เฉพาะบน HTTPS (หรือ
localhost
) เท่านั้น
โค้ดสำหรับลงทะเบียนบนฝั่งเบราว์เซอร์
register-sw.ts
1// register-sw.ts
2async function registerServiceWorker() {
3 if (!('serviceWorker' in navigator)) return;
4
5 try {
6 const registration = await navigator.serviceWorker.register(
7 '/sw.js', { scope: '/' }
8 );
- กระบวนการนี้จะลงทะเบียน
Service Worker
scope
หมายถึง ช่วงเส้นทางที่Service Worker
สามารถควบคุมได้ ตัวอย่างเช่น หากคุณวาง/sw.js
ไว้ที่ root โดยตรงและตั้งค่าscope
เป็นไดเรกทอรี root (/
) คุณจะสามารถควบคุมทรัพยากรทั้งหมดของทั้งเว็บไซต์ได้ ในทางกลับกัน หากคุณระบุไดเรกทอรีเฉพาะเช่น/app/
เฉพาะเนื้อหาภายใต้ไดเรกทอรีนั้นเท่านั้นที่จะถูกควบคุม
1 // If there's a waiting worker, notify the user.
2 if (registration.waiting) {
3 promptUserToUpdate(registration);
4 }
waiting
หมายถึง สถานะที่ Service Worker ใหม่ถูกติดตั้งและกำลังรอที่จะถูกเปิดใช้งาน ในขั้นตอนนี้ หน้าเว็บที่เปิดอยู่ยังคงถูกควบคุมโดยService Worker
เก่าอยู่ จึงมักจะแจ้งผู้ใช้เพื่อขออนุมัติ และเมื่อได้รับการอนุมัติแล้วจึงเรียกใช้skipWaiting()
เพื่อเปิดใช้งานService Worker
ใหม่ทันที วิธีนี้ช่วยให้คุณสามารถอัปเดตกระบวนการล่าสุดได้โดยไม่ต้องรอโหลดหน้าใหม่
1 // When a new SW is installing, monitor its state changes
2 registration.addEventListener('updatefound', () => {
3 const newWorker = registration.installing;
4 if (!newWorker) return;
5 newWorker.addEventListener('statechange', () => {
6 if (newWorker.state === 'installed' &&
7 navigator.serviceWorker.controller) {
8 // New content available, prompt the user
9 promptUserToUpdate(registration);
10 }
11 });
12 });
updatefound
จะถูกเรียกใช้ เมื่อเริ่มต้นติดตั้ง Service Worker ใหม่ เมื่อเหตุการณ์นี้เกิดขึ้น จะมี worker ใหม่ถูกกำหนดไว้ในregistration.installing
ดังนั้นโดยการตรวจสอบstatechange
คุณจะสามารถตรวจสอบได้ว่า การติดตั้งเสร็จสมบูรณ์ (installed
) เมื่อใด นอกจากนี้ หากมีnavigator.serviceWorker.controller
อยู่แล้ว หมายความว่า Service Worker เก่ายังคงควบคุมหน้าอยู่ ซึ่งเป็นโอกาสในการแจ้งผู้ใช้เกี่ยวกับการมีเวอร์ชันใหม่
1 // When the active worker changes (e.g., after skipWaiting), reload if desired
2 navigator.serviceWorker.addEventListener('controllerchange', () => {
3 // Optionally reload to let the new SW take over
4 window.location.reload();
5 });
6 } catch (err) {
7 console.error('Service Worker registration failed: ', err);
8 }
9}
- เหตุการณ์
controllerchange
จะถูกเรียก เมื่อ Service Worker ใหม่เริ่มควบคุมหน้าเว็บปัจจุบัน การโหลดหน้าใหม่ในขั้นตอนนี้จะทำให้กลยุทธ์แคชและการประมวลผลใหม่ถูกนำมาใช้ทันที แต่การรีโหลดอัตโนมัติอาจทำให้ประสบการณ์ผู้ใช้แย่ลง ดังนั้น ควรโหลดหน้าใหม่หลังจากได้รับความยินยอมจากผู้ใช้
1function promptUserToUpdate(reg: ServiceWorkerRegistration) {
2 // Show UI to user. If user accepts:
3 if (reg.waiting) {
4 reg.waiting.postMessage({ type: 'SKIP_WAITING' });
5 }
6}
7
8registerServiceWorker();
- การให้
Service Worker
รับpostMessage({ type: 'SKIP_WAITING' })
จาก client และจากนั้นเรียกใช้self.skipWaiting()
คุณสามารถกระตุ้นให้อัปเดตได้
การประกาศ Scope ใน sw.ts
ต่อไปมาดูตัวอย่าง Service Worker ทั่วไปที่ทำ caching ของ app shell
เมื่อใช้ Service Worker ใน TypeScript การกำหนดชนิดข้อมูลที่ถูกต้องให้กับ self
จะมีประโยชน์
1// sw.ts
2export default null;
3declare const self: ServiceWorkerGlobalScope;
- ใน TypeScript,
self
จะถูกกำหนดเป็นany
โดยค่าเริ่มต้น ดังนั้นหากไม่ได้กำหนดประเภทเพิ่มเติมจะไม่สามารถใช้การเติมข้อความอัตโนมัติหรือตรวจสอบประเภทสำหรับ API เฉพาะของ Service Worker เช่นskipWaiting()
หรือclients
ได้ - หากระบุ
ServiceWorkerGlobalScope
จะช่วยให้ auto-complete ทำงาน ป้องกันการใช้งานผิด และแยกการพัฒนาให้ปลอดภัยกว่าการเขียน script DOM ทั่วไป
Service Worker พื้นฐาน (Install/Activate/Fetch)
ตัวอย่างนี้แสดงการจัดการเวอร์ชันแคชง่ายๆ, โหลดแคชไว้ล่วงหน้าใน install, ลบแคชเก่าเมื่อ activate, และกลยุทธ์แคชใน fetch (cache-first สำหรับไฟล์นิ่ง, network-first สำหรับ API)
sw.ts
(โครงสร้างขั้นต้น + ตัวอย่างโครง Cache)
1const CACHE_NAME = 'app-shell-v1';
2const STATIC_ASSETS = [
3 '/',
4 '/index.html',
5 '/styles.css',
6 '/main.js',
7 '/fallback.png'
8];
9
10self.addEventListener('install', (event: ExtendableEvent) => {
11 // Pre-cache application shell
12 event.waitUntil(
13 caches.open(CACHE_NAME)
14 .then(cache => cache.addAll(STATIC_ASSETS))
15 // Activate immediately (optional: coordinate with client)
16 .then(() => self.skipWaiting())
17 );
18});
- ในระหว่างเหตุการณ์
install
ทรัพยากรคงที่ของแอป (App Shell) จะถูกเก็บไว้ในแคชล่วงหน้า โดยการเรียกใช้self.skipWaiting()
จะทำให้Service Worker
ตัวใหม่ถูกเปิดใช้งานทันที ทำให้แคชล่าสุดพร้อมใช้งานโดยไม่ต้องรอการเข้าถึงครั้งถัดไป
1self.addEventListener('activate', (event: ExtendableEvent) => {
2 // Clean up old caches and take control of clients immediately
3 event.waitUntil(
4 caches.keys().then(keys =>
5 Promise.all(keys
6 .filter(key => key !== CACHE_NAME)
7 .map(key => caches.delete(key)))
8 ).then(() => self.clients.claim())
9 );
10});
- ในเหตุการณ์
activate
แคชเวอร์ชันเก่าจะถูกลบ และทำให้Service Worker
เป็นเวอร์ชันล่าสุดเสมอ นอกจากนี้ โดยการเรียกself.clients.claim()
Service Worker
ตัวใหม่จะสามารถควบคุมลูกค้าทั้งหมดได้โดยไม่ต้องรอให้หน้าโหลดใหม่
1self.addEventListener('fetch', (event: FetchEvent) => {
2 const request = event.request;
3 const url = new URL(request.url);
4
5 // Navigation requests (SPA) -> network-first with fallback to cached index.html
6 if (request.mode === 'navigate') {
7 event.respondWith(
8 fetch(request).catch(() => caches.match('/index.html') as Promise<Response>)
9 );
10 return;
11 }
12
13 // Simple API routing: network-first for /api/
14 if (url.pathname.startsWith('/api/')) {
15 event.respondWith(networkFirst(request));
16 return;
17 }
18
19 // Static assets: cache-first
20 event.respondWith(cacheFirst(request));
21});
- ใน
fetch
สามารถ ดักจับ request และควบคุม response ได้ สามารถกำหนดกลยุทธ์อย่าง cache-first หรือ network-first ซึ่งเป็นประโยชน์ต่อการใช้งานออฟไลน์และประสิทธิภาพ
1self.addEventListener('message', (event: ExtendableMessageEvent) => {
2 const data = (event as any).data;
3 if (!data) return;
4
5 if (data.type === 'SKIP_WAITING') {
6 // Force the waiting service worker to become active
7 self.skipWaiting();
8 }
9});
- หากได้รับ
SKIP_WAITING
การเรียกself.skipWaiting()
จะช่วยให้คุณ เปิดใช้งาน Service Worker ที่กำลังรอได้ทันที ส่งผลให้เวอร์ชันใหม่จะถูกนำไปใช้ตั้งแต่คำร้องขอถัดไปโดยไม่ต้องโหลดหน้าใหม่
ภาพรวมกลยุทธ์ Cache ที่ใช้งานจริง
cache-first
Cache-first จะตรวจเช็คแคชก่อนและคืนค่าทันทีหากมีข้อมูล ถ้าไม่มี ก็จะ fetch จาก network แล้วบันทึกผลลงแคช เหมาะสำหรับไฟล์นิ่ง
1async function cacheFirst(request: Request): Promise<Response> {
2 const cache = await caches.open(CACHE_NAME);
3 const cached = await cache.match(request);
4 if (cached) {
5 return cached;
6 }
7 const response = await fetch(request);
8 if (response && response.ok) {
9 cache.put(request, response.clone());
10 }
11 return response;
12}
- โค้ดนี้คือตัวอย่างการทำงานแบบ cache-first หากมีในแคช จะส่งคืนจากแคช; ถ้าไม่มีจะ fetch จาก network แล้วบันทึกลงแคช เหมาะสำหรับทรัพยากรที่ไม่ค่อยเปลี่ยนแปลง เช่น รูปภาพหรือ CSS
network-first
Network-first จะพยายามเรียกผ่าน network ก่อน และถ้าล้มเหลวจะ fallback ไปที่แคช วิธีนี้เหมาะกับ API ที่ต้องการข้อมูลใหม่ล่าสุด
1async function networkFirst(request: Request): Promise<Response> {
2 const cache = await caches.open(CACHE_NAME);
3 try {
4 const response = await fetch(request);
5 if (response && response.ok) {
6 cache.put(request, response.clone());
7 }
8 return response;
9 } catch (err) {
10 const cached = await cache.match(request);
11 if (cached) return cached;
12 return new Response(JSON.stringify({ error: 'offline' }), {
13 status: 503,
14 headers: { 'Content-Type': 'application/json' }
15 });
16 }
17}
- โค้ดนี้เป็นตัวอย่างการทำงานแบบ network-first ถ้าได้ผลลัพธ์จาก network ก็จะบันทึกไว้ในแคช; ถ้าไม่ได้ ก็จะคืนค่าจากแคช เหมาะสำหรับข้อมูลที่ต้องการความสดใหม่ เช่นข่าวหรือผลลัพธ์จาก API
stale-while-revalidate
stale-while-revalidate จะส่งคืนข้อมูลที่แคชก่อน จากนั้นจึงค่อยอัปเดตข้อมูลใหม่จากเครือข่ายในพื้นหลังไปพร้อมกัน กลยุทธ์นี้สมดุลระหว่างความเร็วในการตอบสนองและความสดใหม่ของข้อมูล
1async function staleWhileRevalidate(request: Request, cacheName = CACHE_NAME): Promise<Response> {
2 const cache = await caches.open(cacheName);
3 const cachedResponse = await cache.match(request);
4 const networkFetch = fetch(request).then(networkResponse => {
5 if (networkResponse && networkResponse.ok) {
6 cache.put(request, networkResponse.clone());
7 }
8 return networkResponse;
9 }).catch(() => undefined);
10
11 // Return cached immediately if exists, otherwise wait network
12 return cachedResponse || (await networkFetch) || new Response('offline', { status: 503 });
13}
- โค้ดนี้จะคืนค่าจากแคชทันทีหากมีข้อมูล พร้อมทั้ง fetch ข้อมูลใหม่จาก network เพื่อนำมาอัปเดตในแคชในเบื้องหลัง วิธีนี้ตอบสนองผู้ใช้งานได้เร็วและใช้เนื้อหาที่อัปเดตในการเข้าถึงครั้งต่อไป เหมาะสำหรับ UI หรือการส่งข้อมูลขนาดเล็ก
การปรับปรุงขั้นตอนการอัปเดต (แจ้งเตือนอัปเดตและรีโหลดอย่างปลอดภัย)
การอัปเดต Service Worker
ไม่ได้เกิดขึ้นทันที; เวอร์ชันใหม่จะยังคงรอจนกว่าจะปิดแท็บที่เปิดอยู่ทั้งหมด
ในที่นี้ เราจะทำระบบเพื่อแจ้ง client เมื่อเวอร์ชันใหม่พร้อมและรีโหลดหน้าโดยปลอดภัยเมื่อผู้ใช้ดำเนินการ
แจ้งเตือนฝั่ง client จากฝั่ง Service Worker
เมื่อเวอร์ชันใหม่พร้อมใช้งาน
1// In sw.ts: after 'activate' or when new version is ready, broadcast a message
2async function notifyClientsUpdated() {
3 const all = await self.clients.matchAll({ type: 'window' });
4 for (const client of all) {
5 client.postMessage({ type: 'SW_UPDATED' });
6 }
7}
8
9// e.g., call this at the end of 'activate'
10self.addEventListener('activate', (event) => {
11 event.waitUntil((async () => {
12 if ('navigationPreload' in self.registration) {
13 await self.registration.navigationPreload.enable();
14 }
15 // cache cleanup
16 const cacheNames = await caches.keys();
17 await Promise.all(
18 cacheNames.map((name) => {
19 if (name !== CACHE_NAME) {
20 return caches.delete(name);
21 }
22 })
23 );
24
25 await self.clients.claim();
26 await notifyClientsUpdated();
27 })());
28});
- ในโค้ดนี้จะเรียก
notifyClientsUpdated
เมื่อจบ eventactivate
เพื่อแจ้งให้ client ทั้งหมดทราบว่าเวอร์ชันใหม่พร้อมแล้วclients.claim()
เป็นเมธอดที่ ทำให้หน้า (client) ที่เปิดอยู่ทั้งหมดถูกควบคุมโดย Service Worker ใหม่ที่เพิ่งถูกเปิดใช้งานทันที โดยปกติService Worker
จะ เริ่มควบคุมหน้าเว็บในครั้งต่อไปที่โหลดเท่านั้น แต่โดยการใช้clients.claim()
คุณสามารถควบคุมหน้าได้ทันทีโดยไม่ต้องโหลดหน้าใหม่
แสดง UI แจ้งเตือนการอัปเดตที่ฝั่ง client และรีโหลดเมื่อผู้ใช้ดำเนินการ
1// in app startup
2navigator.serviceWorker.addEventListener('message', (e) => {
3 if (e.data?.type === 'SW_UPDATED') {
4 // Show a non-intrusive toast or banner: "New version available"
5 // When user clicks "Reload", call:
6 window.location.reload();
7 }
8});
- client จะได้รับ
SW_UPDATED
ผ่าน eventmessage
และแสดงข้อความแจ้งเตือนอัปเดตใน UI เมื่อผู้ใช้เลือกรีโหลด จะมีการเรียกใช้window.location.reload()
ซึ่งจะอัปเดต HTML, CSS และทรัพยากรอื่นๆ บนหน้าเว็บเป็นเวอร์ชันล่าสุด สิ่งนี้รับประกันว่าการแคชและควบคุมโดยService Worker
ที่เปลี่ยนผ่านclients.claim()
จะมีผลต่อหน้าเว็บทั้งหมด
Fallback ขณะออฟไลน์
เตรียม /offline.html
สำหรับการนำทางหลัก และให้ UI ที่เรียบง่ายแต่ยังสื่อความหมายได้แม้ไม่มีภาพหรือฟอนต์ หากเรียก API ล้มเหลว ให้แสดง สถานะที่เก็บไว้ในแคชล่าสุด หากเป็นไปได้ และพยายามเรียกข้อมูลใหม่ในเบื้องหลังเพื่อปรับปรุง UX
ตัวอย่างการนำไปใช้
1// sw.ts
2const CACHE_NAME = 'app-cache-v1';
3
4// Cache offline.html during install
5self.addEventListener('install', (event) => {
6 event.waitUntil((async () => {
7 const cache = await caches.open(CACHE_NAME);
8 await cache.addAll(['/offline.html']);
9 })());
10});
11
12// Handle fetch requests
13self.addEventListener('fetch', (event) => {
14 const request = event.request;
15
16 // Navigation requests (e.g., page transitions)
17 if (request.mode === 'navigate') {
18 event.respondWith((async () => {
19 try {
20 // Try to fetch from the network as usual
21 return await fetch(request);
22 } catch (err) {
23 // On failure, return offline fallback page
24 const cache = await caches.open(CACHE_NAME);
25 return await cache.match('/offline.html') as Response;
26 }
27 })());
28 }
29});
- precaching
/offline.html
ระหว่าง eventinstall
เพื่อให้สามารถตอบกลับหน้าเรียบง่ายเมื่อใช้งานไม่ได้ - ในการเกิดเหตุการณ์
fetch
คุณสามารถตรวจสอบคำขอการนำทางโดยใช้request.mode === 'navigate'
เพื่อเน้นเปลี่ยนหน้าเว็บโดยเฉพาะ - fallback ไปที่
/offline.html
เมื่อเครือข่ายล้มเหลว เพื่อให้สามารถแสดงเนื้อหาได้แม้ออฟไลน์
การสื่อสารข้อความระหว่าง client และ Service Worker
เนื่องจาก Service Worker
ทำงานแยกจากวงจรชีวิตของหน้าเว็บ การสื่อสารแบบสองทิศทาง (bidirectional messaging) จึงสำคัญสำหรับการแจ้งสถานะและดำเนินการคำสั่ง การกำหนดประเภทให้ข้อความจะช่วยป้องกันการส่งข้อความผิดพลาด เปิดใช้งานการเติมโค้ดอัตโนมัติ และทำให้การพัฒนาของคุณเสถียรมากขึ้น
ตัวอย่างโค้ด
- การนิยามชนิดของข้อความ (Message Types)
1type SwToClient =
2 | { type: 'SW_READY' }
3 | { type: 'SW_UPDATED' }
4 | { type: 'CACHE_CLEARED' }
5 | { type: 'PING'; ts: number };
6
7type ClientToSw =
8 | { type: 'CLEAR_CACHE' }
9 | { type: 'PING'; ts: number };
SwToClient
คือชนิดของข้อความที่ส่งจาก Service Worker ไปยัง clientClientToSw
คือชนิดของข้อความที่ส่งจาก client ไปยัง Service Worker- สิ่งนี้ช่วยให้คุณสามารถระบุประเภทของเหตุการณ์ที่สามารถแลกเปลี่ยนกันได้ในการสื่อสารสองทาง
- ขั้นตอนประมวลผลที่ฝั่ง Service Worker
1self.addEventListener('message', (event) => {
2 const data = event.data as ClientToSw;
3 if (data?.type === 'CLEAR_CACHE') {
4 event.waitUntil((async () => {
5 const keys = await caches.keys();
6 await Promise.all(keys.map((k) => caches.delete(k)));
7 await broadcast({ type: 'CACHE_CLEARED' });
8 })());
9 } else if (data?.type === 'PING') {
10 event.source?.postMessage({ type: 'PING', ts: data.ts } as SwToClient);
11 }
12});
- Service Worker จะรับข้อความจาก client และประมวลผลต่อไปตามชนิด (type) ของข้อความ
- ถ้าเป็น
CLEAR_CACHE
จะลบ cache และแจ้ง client ทั้งหมดด้วยCACHE_CLEARED
- ถ้าเป็น
PING
ก็จะตอบกลับไปที่ client ที่ส่งมาด้วยPING
พร้อม timestamp
- แจ้ง client ทั้งหมดจาก Service Worker
1async function broadcast(msg: SwToClient) {
2 const clients = await self.clients.matchAll({ includeUncontrolled: true });
3 for (const c of clients) c.postMessage(msg);
4}
- ใช้
clients.matchAll
เพื่อดึงแท็บ (window) ทั้งหมด - ส่ง
postMessage
ไปแต่ละอันเพื่อ broadcast ข้อความ - สามารถนำไปใช้แจ้งเตือนการอัปเดต (
SW_UPDATED
) หรือข้อความผิดพลาด
- ประมวลผลที่ฝั่ง Client
1navigator.serviceWorker.controller?.postMessage({
2 type: 'PING',
3 ts: Date.now()
4} as ClientToSw);
- โดยการส่ง
PING
จาก client และได้รับการตอบกลับจากService Worker
คุณสามารถตรวจสอบได้ว่าการสื่อสารสองทิศทางทำงานถูกต้อง สิ่งนี้ช่วยให้ง่ายต่อการทดสอบสถานะการเชื่อมต่อและการจัดการข้อความ
1navigator.serviceWorker.addEventListener('message', (e) => {
2 const msg = e.data as SwToClient;
3 switch (msg.type) {
4 case 'SW_READY':
5 console.log('Service Worker is ready');
6 // Example: hide loading spinner or enable offline UI
7 break;
8 case 'SW_UPDATED':
9 console.log('A new version of the Service Worker is available');
10 // Example: show update notification or reload prompt
11 const shouldReload = confirm('A new version is available. Reload now?');
12 if (shouldReload) {
13 window.location.reload();
14 }
15 break;
16 case 'CACHE_CLEARED':
17 console.log('Cache cleared');
18 // Example: show confirmation message to user
19 alert('Cache has been successfully cleared.');
20 break;
21 case 'PING':
22 console.log(`Received PING response, ts=${msg.ts}`);
23 break;
24 }
25});
- {^ i18n_speak
クライアント側では
Service Worker
から送信されるメッセージを受信し、種類に応じて処理を分岐します。SW_READY
は初期化完了、SW_UPDATED
は新バージョン検出、CACHE_CLEARED
はキャッシュ削除完了、PING
は通信確認を示します。各メッセージに応じて、UI の更新やリロード、通知表示などを行います。^}
ข้อดีของการสื่อสารแบบกำหนดชนิดข้อมูล
- การกำหนดชนิดข้อมูลให้กับข้อความทำให้ เห็นอย่างชัดเจนว่ามี message อะไรส่ง-รับได้บ้าง และช่วยให้ auto-complete/ตรวจสอบชนิดข้อมูลเพื่อเพิ่มความปลอดภัย
postMessage
ใช้สำหรับการสื่อสารแบบ หนึ่งต่อหนึ่ง ส่วนbroadcast
สำหรับแบบ หนึ่งต่อหลาย- คุณสามารถพัฒนาฟีเจอร์สำคัญ เช่น แจ้งเตือนอัปเดต (
SW_UPDATED
), จัดการแคช (CACHE_CLEARED
), และตรวจสุขภาพระบบ (PING
) ได้อย่างง่ายดาย
สรุป
- การใช้ TypeScript จะช่วยเพิ่มความปลอดภัยของชนิดข้อมูล (type safety) กับการเรียกใช้ Service Worker API และ messaging ซึ่งส่งผลดีต่อประสิทธิภาพการพัฒนาและการดูแลระบบ
- การเข้าใจ event ในวงจรชีวิต (
install
,activate
, และfetch
) และเลือกกลยุทธ์ cache ที่เหมาะสมกับแต่ละกรณี (เช่น cache-first หรือ network-first) จะช่วยสร้างประสบการณ์ผู้ใช้ที่ดีขึ้น - ในการลงมือทำจริง ควรเข้าใจกระบวนการจัดการเวอร์ชั่น cache และการอัปเดตขั้นตอน (
updatefound
,waiting
,SKIP_WAITING
ฯลฯ) - โดยการนำการสื่อสารแบบมีการกำหนดประเภทมาใช้ในการสื่อสารระหว่าง client กับ
Service Worker
จะช่วยป้องกันการใช้งานผิดพลาด และสร้างระบบที่ขยายและดูแลรักษาได้ง่ายในระยะยาว
คุณสามารถติดตามบทความข้างต้นโดยใช้ Visual Studio Code บนช่อง YouTube ของเรา กรุณาตรวจสอบช่อง YouTube ด้วย