שידור עדכונים לדפים עם Service Workers

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

במקרים מסוימים, יכול להיות ש-service worker יצטרך לתקשר באופן יזום עם אחד מהכרטיסיות הפעילות שהוא שולט בהן כדי לעדכן על אירוע מסוים. דוגמאות:

  • הודעה לדף כשגרסה חדשה של Service Worker מותקנת, כדי שהדף יוכל להציג למשתמש את הלחצן 'עדכון לרענון' כדי לגשת מיד לפונקציונליות החדשה.
  • הודעה למשתמש על שינוי בנתונים שבמטמון שבוצע בצד של Service Worker, באמצעות הצגת אינדיקציה, כמו: "האפליקציה מוכנה עכשיו לעבודה במצב אופליין" או "גרסה חדשה של התוכן זמינה".
דיאגרמה שמציגה קובץ שירות (service worker) שמתקשר עם הדף כדי לשלוח עדכון.

נקרא לסוגים האלה של תרחישי שימוש שבהם ל-service worker לא צריך לקבל הודעה מהדף כדי להתחיל תקשורת 'עדכונים בשידור'. במדריך הזה נסקור דרכים שונות להטמעת סוג התקשורת הזה בין דפים לבין service workers, באמצעות ממשקי API סטנדרטיים של דפדפנים וספריית Workbox.

בקשות תמיכה שקשורות לסביבת ייצור

Tinder

אפליקציית ה-PWA של טינדר משתמשת ב-workbox-window כדי להאזין לרגעים חשובים במחזור החיים של Service Worker מהדף (installed,‏ controlled ו-activated). כך, כש-service worker חדש נכנס לפעולה, מוצג באנר 'יש עדכון', כדי שהמשתמשים יוכלו לרענן את ה-PWA ולקבל גישה לתכונות העדכניות:

צילום מסך של הפונקציונליות 'יש עדכון' באפליקציית האינטרנט של טינדר.
ב-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 מספקת דרך סטנדרטית להודיע ללקוחות של חלונות שתגובה ששמורה במטמון עודכנה. השימוש הנפוץ ביותר הוא ביחד עם האסטרטגיה 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

קובץ השירות יוצר אובייקט BroadcastChannel ומתחיל לשלוח אליו הודעות. כל הקשר (למשל, דף) שמעוניין לקבל את ההודעות האלה יכול ליצור מופע של אובייקט BroadcastChannel ולהטמיע handler של הודעות כדי לקבל הודעות.

כדי להודיע לדף כשמתקינים 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'});
  }
});

בדף מיושם handler של הודעות כדי ליירט את ההודעות האלה:

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

ממשק ה-API של הלקוח הוא אפשרות מצוינת למקרים כמו שידור מידע למספר כרטיסיות פעילות. ‫Storage Access API נתמך בכל הדפדפנים המובילים, אבל לא כל השיטות שלו נתמכות. לפני שמשתמשים בדפדפן, צריך לבדוק אם הוא נתמך.

ערוץ הודעות

Message Channel מחייב שלב הגדרה ראשוני, שבו מעבירים יציאה מהדף אל Service Worker, כדי ליצור ערוץ תקשורת ביניהם. הדף יוצר מופע של אובייקט MessageChannel ומעביר יציאה לקובץ השירות (service worker) דרך הממשק postMessage():

const messageChannel = new MessageChannel();

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

הדף מאזין להודעות באמצעות הטמעה של handler מסוג 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 תהיה מורכבת יותר, כי צריך לאתחל יציאות, אבל היא נתמכת על ידי כל הדפדפנים העיקריים.

השלבים הבאים

במדריך הזה בחנו מקרה ספציפי של תקשורת בין חלון ל-service worker: 'עדכונים של שידור'. הדוגמאות שנבדקו כוללות האזנה לאירועים חשובים במחזור החיים של Service Worker, ותקשור עם הדף לגבי שינויים בתוכן או בנתונים שנשמרו במטמון. אפשר לחשוב על תרחישי שימוש מעניינים יותר שבהם Service Worker מתקשר עם הדף באופן יזום, בלי לקבל הודעה קודמת.

דפוסי תקשורת נוספים בין חלון לבין Service Worker:

מקורות מידע נוספים