เผยแพร่การอัปเดตไปยังหน้าเว็บที่มี Service Worker

Andrew Guan
Andrew Guan

ในบางสถานการณ์ บริการเวิร์กเกอร์อาจต้องสื่อสารกับแท็บที่ใช้งานอยู่ซึ่งควบคุมอยู่เพื่อแจ้งเหตุการณ์บางอย่าง ตัวอย่างเช่น

  • แจ้งให้หน้าเว็บทราบเมื่อติดตั้ง Service Worker เวอร์ชันใหม่แล้ว เพื่อให้หน้าเว็บแสดงปุ่ม "อัปเดตเพื่อรีเฟรช" แก่ผู้ใช้เพื่อเข้าถึงฟังก์ชันการทำงานใหม่ได้ทันที
  • แจ้งให้ผู้ใช้ทราบเกี่ยวกับการเปลี่ยนแปลงในข้อมูลที่แคชไว้ซึ่งเกิดขึ้นฝั่ง Service Worker โดยแสดงตัวบ่งชี้ เช่น "ตอนนี้แอปพร้อมทำงานแบบออฟไลน์แล้ว" หรือ "มีเนื้อหาเวอร์ชันใหม่"
แผนภาพแสดง Service Worker ที่สื่อสารกับหน้าเว็บเพื่อส่งการอัปเดต

เราจะเรียกกรณีการใช้งานประเภทนี้ที่ Service Worker ไม่จำเป็นต้องได้รับข้อความจากหน้าเว็บเพื่อเริ่มการสื่อสารว่า"การประกาศการอัปเดต" ในคู่มือนี้ เราจะดูวิธีต่างๆ ในการใช้การสื่อสารประเภทนี้ระหว่างหน้าเว็บกับ Service Worker โดยใช้ API เบราว์เซอร์มาตรฐานและไลบรารี Workbox

กรณีการใช้งานจริง

Tinder

Tinder PWA ใช้ workbox-window เพื่อฟังช่วงเวลาสําคัญในวงจรบริการของ Service Worker จากหน้าเว็บ ("ติดตั้ง" "ควบคุม" และ "เปิดใช้งาน") วิธีนี้จะช่วยให้ Service Worker ใหม่แสดงแบนเนอร์"อัปเดตพร้อมใช้งาน" เมื่อเริ่มทํางาน เพื่อให้ผู้ใช้รีเฟรช PWA และเข้าถึงฟีเจอร์ล่าสุดได้

ภาพหน้าจอของฟังก์ชัน "อัปเดตพร้อมใช้งาน" ในเว็บแอปของ Tinder
ใน Tinder PWA นั้น Service Worker จะบอกหน้าเว็บว่าเวอร์ชันใหม่พร้อมใช้งานแล้ว และหน้าเว็บจะแสดงแบนเนอร์ "อัปเดตพร้อมใช้งาน" ให้ผู้ใช้เห็น

Squoosh

ใน Squoosh PWA เมื่อ Service Worker แคชชิ้นงานที่จำเป็นทั้งหมดเพื่อให้ทำงานแบบออฟไลน์ได้ ก็จะส่งข้อความไปยังหน้าเว็บเพื่อแสดงข้อความแจ้ง "พร้อมใช้งานแบบออฟไลน์" เพื่อแจ้งให้ผู้ใช้ทราบเกี่ยวกับฟีเจอร์นี้

ภาพหน้าจอของฟังก์ชันการทำงาน "พร้อมใช้งานแบบออฟไลน์" ในเว็บแอป Squoosh
ใน PWA ของ Squoosh Service Worker จะประกาศการอัปเดตหน้าเว็บเมื่อแคชพร้อมใช้งาน และหน้าเว็บจะแสดงข้อความแจ้ง "พร้อมใช้งานแบบออฟไลน์"

การใช้ Workbox

ฟังเหตุการณ์ในวงจรของ Service Worker

workbox-window มีอินเทอร์เฟซที่ใช้งานง่ายในการรับฟังเหตุการณ์สำคัญในวงจร Service Worker ไลบรารีนี้ใช้ API ฝั่งไคลเอ็นต์ เช่น updatefound และ statechange และจัดเตรียมโปรแกรมรับฟังเหตุการณ์ในระดับที่สูงขึ้นในออบเจ็กต์ workbox-window ซึ่งช่วยให้ผู้ใช้ใช้งานเหตุการณ์เหล่านี้ได้ง่ายขึ้น

โค้ดหน้าเว็บต่อไปนี้ช่วยให้คุณตรวจจับได้ทุกครั้งที่มีการติดตั้ง Service Worker เวอร์ชันใหม่ เพื่อให้คุณแจ้งให้ผู้ใช้ทราบ

const wb = new Workbox('/sw.js');

wb.addEventListener('installed', (event) => {
  if (event.isUpdate) {
    // Show "Update App" banner
  }
});

wb.register();

แจ้งให้หน้าเว็บทราบถึงการเปลี่ยนแปลงในข้อมูลแคช

แพ็กเกจ Workbox workbox-broadcast-update มีวิธีมาตรฐานในการแจ้งเตือนไคลเอ็นต์ Window ว่ามีการอัปเดตการตอบกลับที่แคชไว้แล้ว ซึ่งมักใช้ร่วมกับกลยุทธ์ StaleWhileRevalidate

หากต้องการประกาศการอัปเดต ให้เพิ่ม broadcastUpdate.BroadcastUpdatePlugin ลงในตัวเลือกกลยุทธ์ฝั่ง Service Worker

import {registerRoute} from 'workbox-routing';
import {StaleWhileRevalidate} from 'workbox-strategies';
import {BroadcastUpdatePlugin} from 'workbox-broadcast-update';

registerRoute(
  ({url}) => url.pathname.startsWith('/api/'),
  new StaleWhileRevalidate({
    plugins: [
      new BroadcastUpdatePlugin(),
    ],
  })
);

ในเว็บแอป คุณสามารถฟังเหตุการณ์เหล่านี้ได้ดังนี้

navigator.serviceWorker.addEventListener('message', async (event) => {
  // Optional: ensure the message came from workbox-broadcast-update
  if (event.data.meta === 'workbox-broadcast-update') {
    const {cacheName, updatedUrl} = event.data.payload;

    // Do something with cacheName and updatedUrl.
    // For example, get the cached content and update
    // the content on the page.
    const cache = await caches.open(cacheName);
    const updatedResponse = await cache.match(updatedUrl);
    const updatedText = await updatedResponse.text();
  }
});

การใช้ API ของเบราว์เซอร์

หากฟังก์ชันการทำงานของ Workbox ไม่เพียงพอต่อความต้องการ ให้ใช้เบราว์เซอร์ API ต่อไปนี้เพื่อติดตั้งใช้งาน "การออกอากาศการอัปเดต"

Broadcast Channel API

Service Worker จะสร้างออบเจ็กต์ BroadcastChannel และเริ่มส่งข้อความไปยังออบเจ็กต์ดังกล่าว บริบทใดก็ตาม (เช่น หน้าเว็บ) ที่สนใจรับข้อความเหล่านี้สามารถสร้างอินสแตนซ์ของออบเจ็กต์ BroadcastChannel และใช้ตัวแฮนเดิลข้อความเพื่อรับข้อความ

หากต้องการแจ้งให้หน้าเว็บทราบเมื่อติดตั้ง Service Worker ใหม่ ให้ใช้โค้ดต่อไปนี้

// Create Broadcast Channel to send messages to the page
const broadcast = new BroadcastChannel('sw-update-channel');

self.addEventListener('install', function (event) {
  // Inform the page every time a new service worker is installed
  broadcast.postMessage({type: 'CRITICAL_SW_UPDATE'});
});

หน้าเว็บจะรอรับเหตุการณ์เหล่านี้โดยการติดตาม sw-update-channel ดังนี้

// Create Broadcast Channel and listen to messages sent to it
const broadcast = new BroadcastChannel('sw-update-channel');

broadcast.onmessage = (event) => {
  if (event.data && event.data.type === 'CRITICAL_SW_UPDATE') {
    // Show "update to refresh" banner to the user.
  }
};

เทคนิคนี้ง่าย แต่ข้อจํากัดคือความรองรับของเบราว์เซอร์ โดยในขณะที่เขียนบทความนี้ Safari ไม่รองรับ API นี้

Client API

Client API เป็นช่องทางที่สะดวกในการติดต่อกับไคลเอ็นต์หลายรายจาก Service Worker โดยการวนซ้ำอาร์เรย์ออบเจ็กต์ Client

ใช้โค้ด Service Worker ต่อไปนี้เพื่อส่งข้อความไปยังแท็บที่โฟกัสล่าสุด

// Obtain an array of Window client objects
self.clients.matchAll(options).then(function (clients) {
  if (clients && clients.length) {
    // Respond to last focused tab
    clients[0].postMessage({type: 'MSG_ID'});
  }
});

หน้าเว็บใช้ตัวแฮนเดิลข้อความเพื่อสกัดกั้นข้อความเหล่านี้

// Listen to messages
navigator.serviceWorker.onmessage = (event) => {
     if (event.data && event.data.type === 'MSG_ID') {
         // Process response
   }
};

Client API เป็นตัวเลือกที่ยอดเยี่ยมสําหรับกรณีต่างๆ เช่น การกระจายข้อมูลไปยังแท็บที่ใช้งานอยู่หลายแท็บ เบราว์เซอร์หลักทุกตัวรองรับ API นี้ แต่บางเมธอดอาจไม่รองรับ โปรดตรวจสอบการรองรับเบราว์เซอร์ก่อนใช้

ช่องทางของข้อความ

ช่องทางการรับส่งข้อความต้องใช้ขั้นตอนการกำหนดค่าเริ่มต้นด้วยการส่งพอร์ตจากหน้าเว็บไปยัง Service Worker เพื่อสร้างช่องทางการสื่อสารระหว่างหน้าเว็บกับ Service Worker หน้าเว็บสร้างอินสแตนซ์ของออบเจ็กต์ MessageChannel และส่งพอร์ตไปยัง Service Worker ผ่านอินเทอร์เฟซ postMessage() ดังนี้

const messageChannel = new MessageChannel();

// Init port
navigator.serviceWorker.controller.postMessage({type: 'PORT_INITIALIZATION'}, [
  messageChannel.port2,
]);

หน้าเว็บจะฟังข้อความโดยใช้ตัวแฮนเดิล "onmessage" ในพอร์ตนั้น

// Listen to messages
messageChannel.port1.onmessage = (event) => {
  // Process message
};

Service Worker ได้รับพอร์ตและบันทึกการอ้างอิงถึงพอร์ตนั้น

// Initialize
let communicationPort;

self.addEventListener('message', (event) => {
  if (event.data && event.data.type === 'PORT_INITIALIZATION') {
    communicationPort = event.ports[0];
  }
});

จากจุดนั้น โปรแกรมจะส่งข้อความไปยังหน้าเว็บได้โดยเรียกใช้ postMessage() ในข้อมูลอ้างอิงถึงพอร์ต ดังนี้

// Communicate
communicationPort.postMessage({type: 'MSG_ID' });

MessageChannel อาจใช้งานได้ยากกว่าเนื่องจากต้องเริ่มต้นพอร์ต แต่เบราว์เซอร์หลักทั้งหมดรองรับ

ขั้นตอนถัดไป

ในคู่มือนี้ เราจะสำรวจกรณีหนึ่งของการติดต่อระหว่าง Window กับ Service Worker ซึ่งก็คือ "การประกาศอัปเดต" ตัวอย่างที่สำรวจ ได้แก่ การฟังเหตุการณ์วงจรชีวิตของ Service Worker ที่สําคัญ และการสื่อสารกับหน้าเว็บเกี่ยวกับการเปลี่ยนแปลงในเนื้อหาหรือข้อมูลที่แคชไว้ คุณอาจนึกถึงกรณีการใช้งานที่น่าสนใจยิ่งขึ้นได้เมื่อ Service Worker สื่อสารกับหน้าเว็บอย่างสม่ำเสมอโดยที่ก่อนหน้านี้ไม่ได้รับการแจ้งเตือนใดๆ

ดูรูปแบบการสื่อสารเพิ่มเติมของ Window และ Service Worker ได้ที่

แหล่งข้อมูลเพิ่มเติม