Now.js Framework Documentation

Now.js Framework Documentation

ServiceWorkerManager - การจัดการ Service Worker

TH 31 Oct 2025 01:10

ServiceWorkerManager - การจัดการ Service Worker

เอกสารสำหรับ ServiceWorkerManager ซึ่งเป็นระบบจัดการ Service Worker สำหรับการทำงานแบบ offline และ push notifications

📋 สารบัญ

  1. ภาพรวม
  2. การติดตั้งและนำเข้า
  3. การใช้งานพื้นฐาน
  4. การจัดการแคช
  5. กลยุทธ์การแคช
  6. การแจ้งเตือนแบบพุช
  7. การรองรับออฟไลน์
  8. การอัปเดตและวงจรการทำงาน
  9. การตั้งค่า
  10. ตัวอย่างการใช้งาน
  11. เอกสารอ้างอิง api
  12. แนวทางปฏิบัติที่ดี
  13. สรุป

ภาพรวม

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 หลายแบบ:

  1. network-first: ลองดึงจาก network ก่อน ถ้าไม่ได้ใช้ cache
  2. cache-first: ใช้ cache ก่อน ถ้าไม่มีค่อยดึงจาก network
  3. 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 -> Offline

9. ✅ เตรียมทางเลือกเมื่อเบราว์เซอร์ไม่รองรับ

// ✅ ดี: มีทางเลือกเมื่อไม่รองรับ 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
การเพิ่มประสิทธิภาพ ✅ ใช่ ช่วยจัดการแคชอย่างชาญฉลาด
แอปเว็บบนมือถือ ✅ ใช่ เพิ่มประสบการณ์ใช้งาน
เว็บไซต์สแตติก ⚠️ บางส่วน ใช้ได้เฉพาะการแคชพื้นฐาน
แดชบอร์ดผู้ดูแล ⚠️ ระวัง มีข้อมูลอ่อนไหวต้องควบคุมแคช
แอปเรียลไทม์ ⚠️ ระวัง ข้อมูลในแคชอาจไม่ทันสมัย