Now.js Framework Documentation
ServiceWorkerManager - การจัดการ Service Worker
ServiceWorkerManager - การจัดการ Service Worker
เอกสารสำหรับ ServiceWorkerManager ซึ่งเป็นระบบจัดการ Service Worker สำหรับการทำงานแบบ offline และ push notifications
📋 สารบัญ
- ภาพรวม
- การติดตั้งและนำเข้า
- การใช้งานพื้นฐาน
- การจัดการแคช
- กลยุทธ์การแคช
- การแจ้งเตือนแบบพุช
- การรองรับออฟไลน์
- การอัปเดตและวงจรการทำงาน
- การตั้งค่า
- ตัวอย่างการใช้งาน
- เอกสารอ้างอิง api
- แนวทางปฏิบัติที่ดี
- สรุป
ภาพรวม
ServiceWorkerManager เป็นระบบจัดการ Service Worker ที่ช่วยให้แอปพลิเคชันทำงานได้แบบ offline และรองรับ push notifications
คุณสมบัติหลัก
- ✅ การลงทะเบียน Service Worker: ลงทะเบียน service worker อัตโนมัติ
- ✅ การแคชอัตโนมัติ: เก็บไฟล์ JavaScript, CSS และรูปภาพไว้ในแคชให้อัตโนมัติ
- ✅ กลยุทธ์การแคช: รองรับรูปแบบ network-first, cache-first และ stale-while-revalidate
- ✅ การแจ้งเตือนแบบพุช: ใช้งาน push notifications ด้วย VAPID ได้ทันที
- ✅ รองรับการทำงานออฟไลน์: ให้แอปใช้งานได้แม้ไม่มีเครือข่าย
- ✅ อัปเดตอัตโนมัติ: ตรวจสอบและติดตั้ง service worker เวอร์ชันใหม่ให้อัตโนมัติ
- ✅ การแจ้งเตือนเมื่อมีอัปเดต: แจ้งผู้ใช้เมื่อมีเวอร์ชันใหม่พร้อมใช้งาน
- ✅ การจัดการแคช: ล้าง เพิ่ม หรือตั้งค่าการแคชได้ตามต้องการ
- ✅ ระบบอีเวนต์: ปล่อยอีเวนต์ให้ฟังการเปลี่ยนสถานะต่าง ๆ ของ service worker
- ✅ โหมดดีบัก: มีโหมดสำหรับตรวจสอบและแก้ไขในระหว่างพัฒนา
กรณีการใช้งาน
✅ เหมาะสำหรับ:
- แอป Progressive Web App (PWA)
- แอปที่ออกแบบให้ใช้งานออฟไลน์เป็นหลัก
- แอปที่ต้องการระบบแจ้งเตือนแบบพุช
- โปรเจ็กต์ที่ต้องควบคุมการแคชอย่างละเอียด
- งานที่มุ่งเพิ่มประสิทธิภาพการโหลดหน้าเว็บ
- แอปเว็บบนอุปกรณ์พกพา
การติดตั้งและนำเข้า
ServiceWorkerManager โหลดพร้อมกับ Now.js Framework:
// ไม่ต้อง import - พร้อมใช้งานทันที
console.log(window.ServiceWorkerManager); // อ็อบเจ็กต์ ServiceWorkerManager
// เข้าถึงผ่าน Now.js
const swManager = Now.getManager('serviceWorker');ไลบรารีที่ต้องใช้
ServiceWorkerManager ต้องใช้ส่วนประกอบดังต่อไปนี้:
- ErrorManager - สำหรับจัดการข้อผิดพลาด (เลือกใช้งานได้)
- NotificationManager - สำหรับแสดงการแจ้งเตือน (เลือกใช้งานได้)
- EventManager - สำหรับปล่อยอีเวนต์ให้ส่วนอื่นมาฟัง (เลือกใช้งานได้)
- ไฟล์ Service Worker - ต้องมีไฟล์
/service-worker.js
ความต้องการของเบราว์เซอร์
ServiceWorkerManager ต้องการเบราว์เซอร์ที่รองรับ:
- Service Worker API
- PushManager API (สำหรับ push notifications)
- Cache API
// ตรวจสอบว่าเบราว์เซอร์รองรับหรือไม่
if (ServiceWorkerManager.isSupported()) {
console.log('เบราว์เซอร์รองรับ Service Worker');
} else {
console.log('เบราว์เซอร์ไม่รองรับ Service Worker');
}การใช้งานพื้นฐาน
1. เริ่มต้นใช้งาน
// การตั้งค่าเริ่มต้นพื้นฐาน
await ServiceWorkerManager.init({
enabled: true,
serviceWorkerPath: '/service-worker.js',
scope: '/',
version: '1.0.0',
cacheName: 'my-app-cache-v1',
debug: true
});
// ตรวจสอบสถานะ
console.log(ServiceWorkerManager.getStatus());2. ตรวจสอบสถานะ
// ตรวจสอบว่า service worker พร้อมใช้งานแล้วหรือยัง
const status = ServiceWorkerManager.getStatus();
console.log('เริ่มต้นแล้ว:', status.initialized);
console.log('สถานะโดยรวม:', status.status);
console.log('พร้อมใช้งานออฟไลน์:', status.offlineReady);
console.log('เปิดการแจ้งเตือนแบบพุช:', status.pushEnabled);3. ฟัง Events
// ลงทะเบียน service worker สำเร็จ
Now.on('serviceworker:registered', (data) => {
console.log('ลงทะเบียน service worker แล้ว:', data.registration);
});
// พบ service worker เวอร์ชันใหม่
Now.on('serviceworker:update-found', () => {
console.log('มีเวอร์ชันใหม่พร้อมใช้งาน');
});
// สถานะของ service worker เปลี่ยนไป
Now.on('serviceworker:state-change', (data) => {
console.log('สถานะเปลี่ยนเป็น:', data.state);
});
// แอปพร้อมใช้งานในโหมดออฟไลน์
Now.on('serviceworker:offline-ready', () => {
console.log('แอปพร้อมใช้งานแบบออฟไลน์แล้ว');
});
// เกิดข้อผิดพลาดระหว่างทำงาน
Now.on('serviceworker:error', (error) => {
console.error('เกิดข้อผิดพลาดใน service worker:', error);
});4. อัปเดตด้วยตนเอง
// บังคับอัปเดต service worker
await ServiceWorkerManager.update();
// สั่ง skipWaiting และให้ activate ทันที
await ServiceWorkerManager.skipWaiting();การจัดการแคช
การตั้งค่า Precache
// กำหนดไฟล์ที่ต้องการ precache
await ServiceWorkerManager.init({
enabled: true,
precache: [
'/',
'/index.html',
'/css/styles.css',
'/js/app.js',
'/images/logo.png'
]
});รูปแบบไฟล์ที่ต้องการแคช
// กำหนด patterns สำหรับ auto-caching
await ServiceWorkerManager.init({
enabled: true,
cachePatterns: [
/\.js$/, // ไฟล์ JavaScript
/\.css$/, // ไฟล์ CSS
/\.html$/, // ไฟล์ HTML
/\.json$/, // ไฟล์ JSON
/\.png$/, // รูปภาพ PNG
/\.jpe?g$/, // รูปภาพ JPEG
/\.svg$/, // ไฟล์ SVG
/\.woff2?$/, // ฟอนต์สำหรับเว็บ
/\.ttf$/ // ฟอนต์ TrueType
]
});ยกเว้นไม่ให้แคช
// กำหนดไฟล์ที่ไม่ต้องการ cache
await ServiceWorkerManager.init({
enabled: true,
excludeFromCache: [
/\/api\//, // คำขอ API
/analytics/, // ระบบวิเคราะห์ข้อมูล
/\.php$/, // ไฟล์ PHP
/admin/ // หน้าแอดมิน
]
});แคชด้วยตนเอง
// เลือก URL ที่ต้องการแคชด้วยตนเอง
const urls = [
'/page1.html',
'/page2.html',
'/images/banner.jpg'
];
await ServiceWorkerManager.cacheUrls(urls);ล้างแคช
// ลบ cache ทั้งหมด
await ServiceWorkerManager.clearCache();กลยุทธ์การแคช
ประเภทกลยุทธ์
ServiceWorkerManager รองรับ caching strategies หลายแบบ:
- network-first: ลองดึงจาก network ก่อน ถ้าไม่ได้ใช้ cache
- cache-first: ใช้ cache ก่อน ถ้าไม่มีค่อยดึงจาก network
- stale-while-revalidate: ใช้ cache ทันที แต่อัปเดตจาก network ในพื้นหลัง
กำหนดกลยุทธ์
await ServiceWorkerManager.init({
enabled: true,
// Network-first สำหรับ API calls
networkFirst: [
/\/api\//,
/\.json$/
],
// Custom strategies สำหรับ paths เฉพาะ
strategies: {
'/api/': 'network-first', // API - ต้องการข้อมูลสดใหม่
'/images/': 'cache-first', // รูปภาพ - ใช้แคชก่อน
'/css/': 'stale-while-revalidate', // CSS - ใช้แคชไปก่อนแล้วค่อยรีเฟรช
'/js/': 'stale-while-revalidate' // JS - ใช้แคชไปก่อนแล้วค่อยรีเฟรช
}
});ตัวอย่างการตั้งค่ากลยุทธ์
Network First (ดึงจากเครือข่ายก่อน)
// เหมาะสำหรับ API calls และ dynamic content
await ServiceWorkerManager.init({
enabled: true,
strategies: {
'/api/': 'network-first',
'/data/': 'network-first'
}
});Cache First (ใช้แคชก่อน)
// เหมาะสำหรับ static assets
await ServiceWorkerManager.init({
enabled: true,
strategies: {
'/images/': 'cache-first',
'/fonts/': 'cache-first',
'/icons/': 'cache-first'
}
});Stale While Revalidate (ใช้ของเก่าไปก่อนแล้วค่อยรีเฟรช)
// เหมาะสำหรับ content ที่อัปเดตบ่อย
await ServiceWorkerManager.init({
enabled: true,
strategies: {
'/css/': 'stale-while-revalidate',
'/js/': 'stale-while-revalidate'
}
});การแจ้งเตือนแบบพุช
เปิดใช้งานการแจ้งเตือนแบบพุช
// เปิดใช้งานการแจ้งเตือนแบบพุช
await ServiceWorkerManager.init({
enabled: true,
push: {
enabled: true,
publicKey: 'YOUR_VAPID_PUBLIC_KEY',
userVisibleOnly: true
}
});สมัครใช้งานการแจ้งเตือน
// สมัครสมาชิกแจ้งเตือนแบบพุชให้ผู้ใช้
try {
const subscription = await ServiceWorkerManager.subscribePush();
console.log('ข้อมูลการสมัครแจ้งเตือนแบบพุช:', subscription);
// ส่ง subscription ไปที่ server
await sendSubscriptionToServer(subscription);
} catch (error) {
console.error('สมัครรับแจ้งเตือนไม่สำเร็จ:', error);
}ยกเลิกการแจ้งเตือนแบบพุช
// ยกเลิกการสมัครรับแจ้งเตือนแบบพุช
await ServiceWorkerManager.unsubscribePush();
console.log('ยกเลิกการแจ้งเตือนแบบพุชเรียบร้อย');ตรวจสอบสถานะการแจ้งเตือน
// ตรวจสอบว่า push notifications เปิดอยู่หรือไม่
if (ServiceWorkerManager.isPushEnabled()) {
console.log('เปิดการแจ้งเตือนแบบพุชอยู่');
} else {
console.log('ปิดการแจ้งเตือนแบบพุชอยู่');
}อีเวนต์ของการแจ้งเตือนแบบพุช
// สมัครรับการแจ้งเตือนแบบพุชสำเร็จ
Now.on('serviceworker:push-subscribed', (data) => {
console.log('สมัครรับการแจ้งเตือนสำเร็จ:', data.subscription);
// ส่งไปยัง server
sendToServer(data.subscription);
});
// ยกเลิกการแจ้งเตือนแบบพุชสำเร็จ
Now.on('serviceworker:push-unsubscribed', () => {
console.log('ยกเลิกการแจ้งเตือนแบบพุชแล้ว');
});การรองรับออฟไลน์
ตรวจสอบความพร้อมในโหมดออฟไลน์
// ตรวจสอบว่าพร้อมใช้งานในโหมดออฟไลน์หรือยัง
if (ServiceWorkerManager.isOfflineReady()) {
console.log('พร้อมใช้งานในโหมดออฟไลน์แล้ว');
} else {
console.log('ยังไม่พร้อมใช้งานในโหมดออฟไลน์');
}เนื้อหาสำหรับโหมดออฟไลน์
// กำหนดเนื้อหาสำหรับโหมดออฟไลน์
await ServiceWorkerManager.init({
enabled: true,
offlineContent: `
<div class="offline-message">
<h1>คุณออฟไลน์อยู่</h1>
<p>บางฟีเจอร์อาจใช้งานไม่ได้ในตอนนี้</p>
</div>
`
});อีเวนต์เมื่อพร้อมใช้งานออฟไลน์
// ฟังอีเวนต์เมื่อพร้อมใช้งานออฟไลน์
Now.on('serviceworker:offline-ready', () => {
console.log('แอปพร้อมใช้งานออฟไลน์แล้ว');
showOfflineReadyMessage();
});ตรวจจับสถานะเครือข่าย
// ตรวจสอบสถานะเครือข่ายแบบเรียลไทม์
window.addEventListener('online', () => {
console.log('กลับมาออนไลน์แล้ว');
ServiceWorkerManager.update(); // ตรวจสอบการอัปเดต
});
window.addEventListener('offline', () => {
console.log('การเชื่อมต่อเครือข่ายหายไป');
showOfflineMessage();
});การอัปเดตและวงจรการทำงาน
ตรวจสอบอัปเดตอัตโนมัติ
// กำหนดช่วงเวลาในการตรวจสอบการอัปเดต
await ServiceWorkerManager.init({
enabled: true,
updateInterval: 24 * 60 * 60 * 1000, // 24 hours
notifyOnUpdate: true
});ตรวจสอบอัปเดตด้วยตนเอง
// บังคับให้ตรวจสอบการอัปเดตทันที
await ServiceWorkerManager.update();การแจ้งเตือนเมื่อมีอัปเดต
// ระบบจะแสดงการแจ้งเตือนโดยอัตโนมัติเมื่อมีเวอร์ชันใหม่
await ServiceWorkerManager.init({
enabled: true,
notifyOnUpdate: true // แสดงการแจ้งเตือน
});
// ปิดการแจ้งเตือน
await ServiceWorkerManager.init({
enabled: true,
notifyOnUpdate: false
});ข้ามสถานะรอ (skipWaiting)
// บังคับให้ service worker activate ทันที
await ServiceWorkerManager.skipWaiting();
// รีโหลดหน้าเพื่อใช้งานเวอร์ชันใหม่
window.location.reload();อีเวนต์ของวงจรการทำงาน
// สถานะของ service worker เปลี่ยนไป
Now.on('serviceworker:state-change', (data) => {
console.log('สถานะ:', data.state); // 'installed', 'activated', 'redundant'
console.log('สถานะรวม:', data.status);
});
// พบการอัปเดตใหม่
Now.on('serviceworker:update-found', () => {
console.log('กำลังติดตั้งเวอร์ชันใหม่');
});
// ลงทะเบียน service worker แล้ว
Now.on('serviceworker:registered', (data) => {
console.log('ลงทะเบียน service worker สำเร็จ');
});
// ยกเลิกการลงทะเบียน service worker
Now.on('serviceworker:unregistered', () => {
console.log('ยกเลิกการลงทะเบียน service worker แล้ว');
});ยกเลิกการลงทะเบียน Service Worker
// ยกเลิกการลงทะเบียน service worker
const success = await ServiceWorkerManager.unregister();
if (success) {
console.log('ยกเลิกการลงทะเบียน service worker สำเร็จ');
}การตั้งค่า
ตัวเลือกการตั้งค่า
const config = {
// เปิด/ปิด service worker
enabled: true,
// ตำแหน่งของ service worker file
serviceWorkerPath: '/service-worker.js',
// Scope ของ service worker
scope: '/',
// เวอร์ชันของ service worker
version: '1.0.0',
// ชื่อแคช
cacheName: 'my-app-cache-v1',
// เปิดโหมดดีบักหรือไม่
debug: true,
// ช่วงเวลาในการตรวจสอบอัปเดต (มิลลิวินาที)
updateInterval: 24 * 60 * 60 * 1000, // 24 hours
// รายการ URL ที่ต้องการ precache
precache: [
'/',
'/index.html',
'/css/styles.css',
'/js/app.js'
],
// เปิดให้แคชไฟล์ JavaScript อัตโนมัติ
cacheJavascriptFiles: true,
// รูปแบบไฟล์ที่ต้องการให้แคชอัตโนมัติ
cachePatterns: [
/\.js$/,
/\.css$/,
/\.html$/,
/\.json$/,
/\.png$/,
/\.jpe?g$/,
/\.svg$/,
/\.woff2?$/,
/\.ttf$/
],
// รูปแบบ URL ที่ต้องดึงจากเครือข่ายก่อน
networkFirst: [
/\/api\//,
/\.json$/
],
// รูปแบบที่ไม่ต้องการให้แคช
excludeFromCache: [
/analytics/,
/\.php$/
],
// แจ้งผู้ใช้เมื่อมีเวอร์ชันใหม่
notifyOnUpdate: true,
// เนื้อหาหน้าออฟไลน์ (ถ้าไม่ตั้งค่าจะใช้ดีฟอลต์)
offlineContent: null,
// การตั้งค่าการแจ้งเตือนแบบพุช
push: {
enabled: false,
publicKey: null,
userVisibleOnly: true
},
// กำหนดกลยุทธ์การแคชตามเส้นทาง
strategies: {
'/api/': 'network-first',
'/images/': 'cache-first',
'/css/': 'stale-while-revalidate'
}
};
await ServiceWorkerManager.init(config);ตัวอย่างการใช้งาน
1. Basic PWA Setup
// ตั้งค่า service worker สำหรับ PWA
await ServiceWorkerManager.init({
enabled: true,
serviceWorkerPath: '/service-worker.js',
scope: '/',
version: '1.0.0',
cacheName: 'pwa-cache-v1',
debug: false,
precache: [
'/',
'/index.html',
'/css/styles.css',
'/js/app.js',
'/manifest.json',
'/images/icon-192.png',
'/images/icon-512.png'
],
notifyOnUpdate: true
});
// ฟังอีเวนต์เมื่อพร้อมใช้งานออฟไลน์
Now.on('serviceworker:offline-ready', () => {
showNotification('พร้อมใช้งานแบบออฟไลน์แล้ว!');
});2. E-commerce Application
// ตัวอย่างอีคอมเมิร์ซที่บริหารแคชอย่างฉลาด
await ServiceWorkerManager.init({
enabled: true,
cacheName: 'ecommerce-cache-v1',
// Precache หน้าและเพจสำคัญ
precache: [
'/',
'/products',
'/cart',
'/checkout'
],
// กำหนดกลยุทธ์การแคช
strategies: {
'/api/products': 'network-first', // สินค้า - ต้องการข้อมูลล่าสุด
'/api/cart': 'network-first', // ตะกร้าสินค้า - ต้องอัปเดตตลอด
'/images/products/': 'cache-first', // รูปสินค้า - ใช้แคชได้
'/static/': 'cache-first', // แอสเซ็ตคงที่ - ใช้แคชได้
'/css/': 'stale-while-revalidate', // CSS - ใช้ของเดิมได้ระหว่างรีเฟรช
'/js/': 'stale-while-revalidate' // JS - ใช้ของเดิมได้ระหว่างรีเฟรช
},
// ไม่แคชหน้า checkout
excludeFromCache: [
/\/api\/checkout/,
/\/api\/payment/
],
// แจ้งเตือนสถานะคำสั่งซื้อผ่าน push notification
push: {
enabled: true,
publicKey: 'YOUR_VAPID_PUBLIC_KEY'
}
});
// สมัครรับการแจ้งเตือนคำสั่งซื้อ
const subscription = await ServiceWorkerManager.subscribePush();
await api.saveSubscription(subscription);3. News/Blog Application
// แอปข่าวที่อ่านออฟไลน์ได้
await ServiceWorkerManager.init({
enabled: true,
cacheName: 'news-cache-v1',
// รูปแบบไฟล์ที่ต้องการแคช
cachePatterns: [
/\.html$/,
/\.css$/,
/\.js$/,
/\/articles\/.+/, // หน้าเนื้อหาข่าว
/\/images\/.+/ // รูปภาพ
],
// ใช้ network-first เพื่อดึงข่าวล่าสุด
networkFirst: [
/\/api\/articles/,
/\/api\/latest/
],
// ใช้ stale-while-revalidate สำหรับบทความ
strategies: {
'/articles/': 'stale-while-revalidate',
'/images/': 'cache-first'
},
// ตรวจสอบอัปเดตทุกชั่วโมง
updateInterval: 60 * 60 * 1000,
// แจ้งเมื่อมีบทความใหม่
notifyOnUpdate: true
});
// สมัครรับการแจ้งเตือนข่าวด่วน
await ServiceWorkerManager.subscribePush();4. Dashboard Application
// แดชบอร์ดที่แคช API อย่างเหมาะสม
await ServiceWorkerManager.init({
enabled: true,
cacheName: 'dashboard-cache-v1',
// Precache หน้าสำคัญของแดชบอร์ด
precache: [
'/dashboard',
'/dashboard/analytics',
'/dashboard/reports',
'/dashboard/settings'
],
// ใช้ network-first สำหรับข้อมูลสด
networkFirst: [
/\/api\/dashboard/,
/\/api\/analytics/,
/\.json$/
],
// แคชแอสเซ็ตที่เป็นไฟล์คงที่
strategies: {
'/static/': 'cache-first',
'/charts/': 'cache-first',
'/api/': 'network-first'
},
// ไม่แคชข้อมูลที่มีความอ่อนไหว
excludeFromCache: [
/\/api\/user\/sensitive/,
/\/api\/admin/
],
debug: true
});
// จัดการเมื่อเข้าโหมดออฟไลน์
Now.on('serviceworker:offline-ready', () => {
console.log('แดชบอร์ดพร้อมใช้งานแบบออฟไลน์');
});
window.addEventListener('offline', () => {
showBanner('คุณออฟไลน์อยู่ บางฟีเจอร์อาจใช้งานไม่ได้');
});5. Custom Update Handling
// แสดงกล่องแจ้งเตือนอัปเดตแบบกำหนดเอง
await ServiceWorkerManager.init({
enabled: true,
notifyOnUpdate: false // ปิดการแจ้งเตือนแบบดีฟอลต์
});
// สร้าง UI แจ้งเตือนอัปเดตตามต้องการ
Now.on('serviceworker:update-found', () => {
showUpdateDialog({
title: 'มีเวอร์ชันใหม่พร้อมใช้งาน',
message: 'เวอร์ชันใหม่พร้อมแล้ว ต้องการอัปเดตเลยหรือไม่?',
onConfirm: async () => {
await ServiceWorkerManager.skipWaiting();
window.location.reload();
},
onCancel: () => {
console.log('เลื่อนการอัปเดตออกไป');
}
});
});6. Cache Management UI
// อินเทอร์เฟซสำหรับจัดการแคช
class CacheManager {
async init() {
await ServiceWorkerManager.init({
enabled: true,
debug: true
});
this.renderUI();
}
renderUI() {
const container = document.querySelector('#cache-manager');
container.innerHTML = `
<h2>จัดการแคช</h2>
<div class="cache-info">
<p>สถานะ: <span id="cache-status"></span></p>
<p>พร้อมใช้งานออฟไลน์: <span id="offline-status"></span></p>
</div>
<div class="cache-actions">
<button id="clear-cache">ล้างแคช</button>
<button id="update-sw">ตรวจสอบการอัปเดต</button>
<button id="cache-urls">แคชหน้าปัจจุบัน</button>
</div>
`;
this.updateStatus();
this.setupEvents();
}
updateStatus() {
const status = ServiceWorkerManager.getStatus();
document.querySelector('#cache-status').textContent = status.status;
document.querySelector('#offline-status').textContent =
status.offlineReady ? 'พร้อม' : 'ไม่พร้อม';
}
setupEvents() {
document.querySelector('#clear-cache').addEventListener('click', async () => {
await ServiceWorkerManager.clearCache();
alert('ล้างแคชเรียบร้อย!');
this.updateStatus();
});
document.querySelector('#update-sw').addEventListener('click', async () => {
await ServiceWorkerManager.update();
alert('กำลังตรวจสอบการอัปเดต...');
});
document.querySelector('#cache-urls').addEventListener('click', async () => {
const currentUrl = window.location.href;
await ServiceWorkerManager.cacheUrls([currentUrl]);
alert('แคชหน้าปัจจุบันเรียบร้อย!');
});
}
}
const cacheManager = new CacheManager();
cacheManager.init();7. Progressive Enhancement
// ตรวจสอบการรองรับแล้วค่อยเปิดฟีเจอร์ทีละขั้น
async function initializeApp() {
// ตรวจสอบว่าเบราว์เซอร์รองรับ service worker หรือไม่
if (!ServiceWorkerManager.isSupported()) {
console.log('ไม่รองรับ Service Worker จะทำงานแบบออนไลน์เท่านั้น');
return;
}
// ตั้งค่า service worker
await ServiceWorkerManager.init({
enabled: true,
cacheName: 'app-cache-v1',
debug: true
});
// เปิดฟีเจอร์สำหรับใช้งานออฟไลน์
Now.on('serviceworker:offline-ready', () => {
enableOfflineFeatures();
showOfflineBadge();
});
// เปิดการแจ้งเตือนแบบพุชหากรองรับ
if ('PushManager' in window) {
await setupPushNotifications();
}
}
async function setupPushNotifications() {
await ServiceWorkerManager.init({
push: {
enabled: true,
publicKey: 'YOUR_VAPID_PUBLIC_KEY'
}
});
// ขอสิทธิ์การแจ้งเตือนจากผู้ใช้
const permission = await Notification.requestPermission();
if (permission === 'granted') {
const subscription = await ServiceWorkerManager.subscribePush();
await sendSubscriptionToServer(subscription);
}
}
initializeApp();8. Network Status Indicator
// แสดงสถานะเครือข่ายร่วมกับ service worker
class NetworkStatus {
constructor() {
this.indicator = null;
this.init();
}
async init() {
await ServiceWorkerManager.init({
enabled: true
});
this.createIndicator();
this.setupListeners();
}
createIndicator() {
this.indicator = document.createElement('div');
this.indicator.className = 'network-indicator';
document.body.appendChild(this.indicator);
this.updateIndicator();
}
setupListeners() {
window.addEventListener('online', () => {
this.updateIndicator();
ServiceWorkerManager.update();
});
window.addEventListener('offline', () => {
this.updateIndicator();
});
Now.on('serviceworker:offline-ready', () => {
this.updateIndicator();
});
}
updateIndicator() {
const isOnline = navigator.onLine;
const isOfflineReady = ServiceWorkerManager.isOfflineReady();
if (isOnline) {
this.indicator.textContent = '● ออนไลน์';
this.indicator.className = 'network-indicator online';
} else if (isOfflineReady) {
this.indicator.textContent = '● ออฟไลน์ (มีแคช)';
this.indicator.className = 'network-indicator offline-ready';
} else {
this.indicator.textContent = '● ออฟไลน์';
this.indicator.className = 'network-indicator offline';
}
}
}
new NetworkStatus();9. Background Sync
// Background sync for form submissions
class BackgroundSync {
constructor() {
this.pendingRequests = [];
this.init();
}
async init() {
await ServiceWorkerManager.init({
enabled: true
});
// ฟังเหตุการณ์เมื่อกลับมาออนไลน์
window.addEventListener('online', () => {
this.syncPendingRequests();
});
}
async submitForm(formData) {
if (navigator.onLine) {
// ออนไลน์อยู่ - ส่งข้อมูลได้เลย
return await this.sendRequest(formData);
} else {
// ออฟไลน์ - เก็บไว้ส่งภายหลัง
this.pendingRequests.push(formData);
this.savePendingRequests();
return {queued: true};
}
}
async syncPendingRequests() {
const requests = this.loadPendingRequests();
for (const request of requests) {
try {
await this.sendRequest(request);
this.removePendingRequest(request);
} catch (error) {
console.error('ซิงก์ข้อมูลไม่สำเร็จ:', error);
}
}
}
async sendRequest(data) {
// ส่งข้อมูลไปยังเซิร์ฟเวอร์
return await fetch('/api/submit', {
method: 'POST',
body: JSON.stringify(data)
});
}
savePendingRequests() {
localStorage.setItem('pendingRequests',
JSON.stringify(this.pendingRequests));
}
loadPendingRequests() {
const data = localStorage.getItem('pendingRequests');
return data ? JSON.parse(data) : [];
}
removePendingRequest(request) {
this.pendingRequests = this.pendingRequests.filter(r => r !== request);
this.savePendingRequests();
}
}
const bgSync = new BackgroundSync();10. Version Management
// จัดการเวอร์ชันและการย้ายข้อมูล
class VersionManager {
async init() {
const currentVersion = '2.0.0';
const cachedVersion = localStorage.getItem('app-version');
await ServiceWorkerManager.init({
enabled: true,
version: currentVersion,
cacheName: `app-cache-${currentVersion}`
});
// ตรวจสอบว่ามีการอัปเกรดเวอร์ชันหรือไม่
if (cachedVersion && cachedVersion !== currentVersion) {
await this.migrateVersion(cachedVersion, currentVersion);
}
localStorage.setItem('app-version', currentVersion);
}
async migrateVersion(oldVersion, newVersion) {
console.log(`กำลังย้ายเวอร์ชันจาก ${oldVersion} ไปยัง ${newVersion}`);
// ล้างแคชเวอร์ชันเก่า
await ServiceWorkerManager.clearCache();
// ยกเลิกการลงทะเบียน service worker ตัวเก่า
await ServiceWorkerManager.unregister();
// ลงทะเบียน service worker ตัวใหม่
await ServiceWorkerManager.init({
enabled: true,
version: newVersion,
cacheName: `app-cache-${newVersion}`
});
// ย้ายข้อมูลเพิ่มเติมถ้าจำเป็น
await this.migrateData(oldVersion, newVersion);
// แจ้งผู้ใช้
showNotification(`อัปเกรดเป็นเวอร์ชัน ${newVersion} แล้ว`);
}
async migrateData(oldVersion, newVersion) {
// ใส่ขั้นตอนย้ายข้อมูลเพิ่มเติมที่ต้องการ
console.log('กำลังย้ายข้อมูล...');
}
}
const versionManager = new VersionManager();
versionManager.init();เอกสารอ้างอิง API
การตั้งค่า
{
enabled: boolean, // เปิดหรือปิด service worker
serviceWorkerPath: string, // ที่อยู่ไฟล์ service worker
scope: string, // ขอบเขตการทำงานของ service worker
version: string, // เวอร์ชันของ service worker
cacheName: string, // ชื่อแคชที่ใช้เก็บข้อมูล
debug: boolean, // เปิดโหมดดีบักหรือไม่
updateInterval: number, // ช่วงเวลาเช็กอัปเดต (มิลลิวินาที)
precache: string[], // รายการ URL ที่ต้อง precache
cacheJavascriptFiles: boolean, // แคชไฟล์ JS อัตโนมัติหรือไม่
cachePatterns: RegExp[], // รูปแบบไฟล์ที่ต้องการแคช
networkFirst: RegExp[], // รูปแบบที่ควรดึงจากเน็ตก่อน
excludeFromCache: RegExp[], // รูปแบบที่ไม่ต้องการแคช
notifyOnUpdate: boolean, // แจ้งเตือนเมื่อมีเวอร์ชันใหม่
offlineContent: string | null, // เนื้อหา HTML สำหรับโหมดออฟไลน์
push: {
enabled: boolean, // เปิดการแจ้งเตือนแบบพุช
publicKey: string | null, // คีย์สาธารณะ VAPID
userVisibleOnly: boolean // ต้องแสดงการแจ้งเตือนต่อผู้ใช้เท่านั้น
},
strategies: { // ตั้งค่ากลยุทธ์การแคชรายเส้นทาง
[path: string]: 'network-first' | 'cache-first' | 'stale-while-revalidate'
}
}เมธอดที่รองรับ
การเริ่มต้นใช้งาน
// เริ่มต้นใช้งาน service worker
init(options?: object): Promise<ServiceWorkerManager>
// ตรวจสอบว่าเบราว์เซอร์รองรับหรือไม่
isSupported(): booleanการจัดการแคช
// แคช URL ด้วยตนเอง
cacheUrls(urls: string[]): Promise<boolean>
// ล้างแคช
clearCache(): Promise<boolean>การจัดการอัปเดต
// ตรวจสอบการอัปเดต
update(): Promise<boolean>
// เรียกใช้ skipWaiting แล้ว activate
skipWaiting(): Promise<boolean>
// ยกเลิกการลงทะเบียน service worker
unregister(): Promise<boolean>การแจ้งเตือนแบบพุช
// สมัครรับการแจ้งเตือนแบบพุช
subscribePush(): Promise<PushSubscription>
// ยกเลิกการแจ้งเตือนแบบพุช
unsubscribePush(): Promise<boolean>
// ตรวจสอบว่าเปิดการแจ้งเตือนแบบพุชหรือไม่
isPushEnabled(): booleanตรวจสอบสถานะ
// ตรวจสอบว่าพร้อมใช้งานออฟไลน์หรือยัง
isOfflineReady(): boolean
// ดึงสถานะปัจจุบัน
getStatus(): {
supported: boolean,
initialized: boolean,
status: string,
offlineReady: boolean,
registration: ServiceWorkerRegistration | null,
lastUpdateCheck: number,
errors: number,
pushEnabled: boolean
}อีเวนต์ที่มีให้ใช้งาน
// ลงทะเบียน service worker
'serviceworker:registered' -> {
registration: ServiceWorkerRegistration
}
// พบการอัปเดตใหม่
'serviceworker:update-found' -> {
registration: ServiceWorkerRegistration
}
// สถานะ service worker เปลี่ยนไป
'serviceworker:state-change' -> {
state: string,
status: string
}
// พร้อมใช้งานออฟไลน์
'serviceworker:offline-ready'
// เกิดข้อผิดพลาด
'serviceworker:error' -> {
message: string,
stack: string,
context: string,
timestamp: number
}
// สมัครรับการแจ้งเตือนแบบพุชสำเร็จ
'serviceworker:push-subscribed' -> {
subscription: PushSubscription
}
// ยกเลิกการแจ้งเตือนแบบพุช
'serviceworker:push-unsubscribed'
// ข้อความที่ส่งมาจาก service worker
'serviceworker:message' -> {
type: string,
payload: any
}
// ยกเลิกการลงทะเบียน service worker
'serviceworker:unregistered'สถานะภายใน
{
initialized: boolean, // สถานะว่าเริ่มต้นแล้วหรือไม่
registration: ServiceWorkerRegistration | null, // ข้อมูลการลงทะเบียน
updateFound: boolean, // พบการอัปเดตใหม่หรือยัง
installingWorker: ServiceWorker | null, // ตัว worker ที่กำลังติดตั้ง
offlineReady: boolean, // พร้อมใช้งานออฟไลน์หรือไม่
lastUpdateCheck: number, // เวลาที่เช็กอัปเดตล่าสุด (timestamp)
status: string, // สถานะปัจจุบัน
errors: Array, // ประวัติข้อผิดพลาด
pushEnabled: boolean, // เปิดการแจ้งเตือนแบบพุชหรือไม่
pushSubscription: PushSubscription | null // ข้อมูลการสมัคร push ปัจจุบัน
}แนวทางปฏิบัติที่ดี
1. ✅ ใช้กลยุทธ์การแคชให้เหมาะกับเนื้อหา
// ✅ ดี: ตั้งกลยุทธ์ให้เหมาะกับประเภทข้อมูลแต่ละแบบ
await ServiceWorkerManager.init({
strategies: {
'/api/': 'network-first', // API - ต้องการข้อมูลสดใหม่
'/images/': 'cache-first', // รูปภาพ - ใช้แคชก่อน
'/css/': 'stale-while-revalidate' // CSS - ใช้แคชชั่วคราวได้
}
});
// ❌ ไม่ดี: ใช้กลยุทธ์เดียวกับทุกเส้นทาง
await ServiceWorkerManager.init({
strategies: {
'/': 'cache-first'
}
});2. ✅ Precache ไฟล์สำคัญล่วงหน้า
// ✅ ดี: precache ไฟล์สำคัญที่ต้องใช้ทันที
await ServiceWorkerManager.init({
precache: [
'/',
'/index.html',
'/css/critical.css',
'/js/app.js',
'/manifest.json'
]
});
// ❌ ไม่ดี: precache ไฟล์มากเกินไป
await ServiceWorkerManager.init({
precache: [
// ไฟล์มากกว่า 100 รายการ จะทำให้โหลดครั้งแรกช้าลง
]
});3. ✅ ตั้งชื่อแคชพร้อมเวอร์ชัน
// ✅ ดี: เพิ่มเวอร์ชันในชื่อแคช
await ServiceWorkerManager.init({
version: '1.0.0',
cacheName: 'my-app-cache-v1'
});
// เมื่ออัปเดตเวอร์ชัน:
await ServiceWorkerManager.init({
version: '1.1.0',
cacheName: 'my-app-cache-v1.1'
});4. ✅ อย่าแคชข้อมูลที่มีความอ่อนไหว
// ✅ ดี: ไม่แคชข้อมูลที่ต้องปกป้อง
await ServiceWorkerManager.init({
excludeFromCache: [
/\/api\/user\/sensitive/,
/\/api\/admin/,
/\/api\/payment/,
/\.php$/
]
});5. ✅ จัดการการอัปเดตอย่างนุ่มนวล
// ✅ ดี: แจ้งผู้ใช้แล้วให้เลือกว่าจะอัปเดตหรือไม่
Now.on('serviceworker:update-found', () => {
showNotification('มีเวอร์ชันใหม่พร้อมใช้งาน', {
action: 'รีโหลด',
onAction: () => {
ServiceWorkerManager.skipWaiting();
window.location.reload();
}
});
});
// ❌ ไม่ดี: บังคับรีโหลดทันทีที่มีอัปเดต
Now.on('serviceworker:update-found', () => {
window.location.reload();
});6. ✅ เปิดโหมดดีบักเฉพาะตอนพัฒนา
// ✅ ดี: เปิดดีบักเฉพาะในสภาพแวดล้อมพัฒนา
const isDev = window.location.hostname === 'localhost';
await ServiceWorkerManager.init({
debug: isDev,
updateInterval: isDev ? 10000 : 86400000 // 10s vs 24h
});7. ✅ เฝ้าระวังข้อผิดพลาด
// ✅ ดี: ติดตามข้อผิดพลาดเพื่อแก้ปัญหา
Now.on('serviceworker:error', (error) => {
console.error('เกิดข้อผิดพลาดใน service worker:', error);
// ส่งต่อไปยังระบบติดตามข้อผิดพลาด
if (window.ErrorTracker) {
ErrorTracker.log(error);
}
});8. ✅ ทดสอบการทำงานในโหมดออฟไลน์
// ✅ ดี: ทดสอบการใช้งานขณะออฟไลน์
if (ServiceWorkerManager.isOfflineReady()) {
console.log('✓ พร้อมใช้งานออฟไลน์');
} else {
console.warn('✗ ยังไม่พร้อมใช้งานออฟไลน์');
}
// ทดสอบด้วยตัวเอง
// Chrome DevTools -> Application -> Service Workers -> Offline9. ✅ เตรียมทางเลือกเมื่อเบราว์เซอร์ไม่รองรับ
// ✅ ดี: มีทางเลือกเมื่อไม่รองรับ service worker
if (ServiceWorkerManager.isSupported()) {
await ServiceWorkerManager.init({
enabled: true
});
} else {
console.log('ไม่รองรับ Service Worker - ใช้งานได้เฉพาะโหมดออนไลน์');
initializeOnlineOnlyMode();
}10. ✅ ล้างแคชเก่าเมื่อเปลี่ยนเวอร์ชัน
// ✅ ดี: ล้างแคชเก่าทุกครั้งที่เปลี่ยนเวอร์ชัน
const currentVersion = '2.0.0';
const storedVersion = localStorage.getItem('app-version');
if (storedVersion && storedVersion !== currentVersion) {
await ServiceWorkerManager.clearCache();
await ServiceWorkerManager.unregister();
// ลงทะเบียนใหม่ด้วยเวอร์ชันปัจจุบัน
await ServiceWorkerManager.init({
version: currentVersion,
cacheName: `app-cache-${currentVersion}`
});
localStorage.setItem('app-version', currentVersion);
}สรุป
คุณสมบัติหลัก
| คุณสมบัติ | รองรับ | หมายเหตุ |
|---|---|---|
| Service Worker Registration | ✅ | ลงทะเบียนอัตโนมัติ |
| Auto Caching | ✅ | แคชตามรูปแบบไฟล์ที่กำหนด |
| Caching Strategies | ✅ | รองรับ network-first, cache-first, stale-while-revalidate |
| Push Notifications | ✅ | รองรับ VAPID |
| Offline Support | ✅ | ใช้งานได้เต็มรูปแบบแม้ออฟไลน์ |
| Auto Updates | ✅ | ตั้งช่วงเวลาตรวจสอบได้ |
| Update Notifications | ✅ | แจ้งเตือนผู้ใช้เมื่อมีเวอร์ชันใหม่ |
| Cache Management | ✅ | ควบคุมแคชได้ด้วยตัวเอง |
| Event System | ✅ | มีอีเวนต์สำหรับแต่ละช่วงชีวิต |
| Debug Mode | ✅ | มีเครื่องมือช่วยในช่วงพัฒนา |
เมื่อใดควรใช้ ServiceWorkerManager
| กรณีใช้งาน | ใช้ ServiceWorkerManager? | หมายเหตุ |
|---|---|---|
| Progressive Web App | ✅ ใช่ | กรณีใช้งานหลัก |
| การทำงานแบบออฟไลน์ | ✅ ใช่ | รองรับออฟไลน์เต็มรูปแบบ |
| การแจ้งเตือนแบบพุช | ✅ ใช่ | รองรับ VAPID |
| การเพิ่มประสิทธิภาพ | ✅ ใช่ | ช่วยจัดการแคชอย่างชาญฉลาด |
| แอปเว็บบนมือถือ | ✅ ใช่ | เพิ่มประสบการณ์ใช้งาน |
| เว็บไซต์สแตติก | ⚠️ บางส่วน | ใช้ได้เฉพาะการแคชพื้นฐาน |
| แดชบอร์ดผู้ดูแล | ⚠️ ระวัง | มีข้อมูลอ่อนไหวต้องควบคุมแคช |
| แอปเรียลไทม์ | ⚠️ ระวัง | ข้อมูลในแคชอาจไม่ทันสมัย |