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

Andrew Guan
Andrew Guan
Demián Renzulli
Demián Renzulli

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

  • แจ้งให้หน้าเว็บทราบเมื่อมีการติดตั้ง 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
ใน Squoosh PWA นั้น 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 มีวิธีมาตรฐานในการแจ้งให้ไคลเอ็นต์หน้าต่างทราบว่ามีการอัปเดตการตอบกลับที่แคชไว้ โดยส่วนใหญ่มักใช้ร่วมกับกลยุทธ์ 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 object และเริ่มส่ง ข้อความไปยังออบเจ็กต์ดังกล่าว บริบทใดๆ (เช่น หน้าเว็บ) ที่สนใจรับข้อความเหล่านี้สามารถสร้างอินสแตนซ์ออบเจ็กต์ 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 นี้ได้รับการรองรับจากเบราว์เซอร์หลักๆ ทั้งหมด แต่ไม่ใช่ทุกเมธอด โปรดตรวจสอบการรองรับของเบราว์เซอร์ก่อนใช้งาน

Message Channel

Message Channel ต้องมี ขั้นตอนการกำหนดค่าเริ่มต้นโดยการส่งพอร์ตจากหน้าเว็บไปยัง 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];
  }
});

จากนั้น Service Worker จะส่งข้อความไปยังหน้าเว็บได้โดยการเรียก postMessage() ในการอ้างอิงไปยังพอร์ต

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

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

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

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

หากต้องการดูรูปแบบเพิ่มเติมของการสื่อสารระหว่างหน้าต่างกับ Service Worker โปรดดูที่

  • คู่มือการแคชแบบบังคับ: การเรียก Service Worker จากหน้าเว็บเพื่อ แคชทรัพยากรล่วงหน้า (เช่น ในสถานการณ์การดึงข้อมูลล่วงหน้า)
  • การสื่อสารแบบ 2 ทาง: การมอบหมายงานให้ Service Worker (เช่น การดาวน์โหลดขนาดใหญ่) และแจ้งให้หน้าเว็บทราบความคืบหน้า

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