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

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

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

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

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

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

Tinder

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

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

סקווש

ב-Squoosh PWA, כשעובד השירות שומר במטמון את כל הנכסים הנחוצים כדי שהאפליקציה תפעל במצב אופליין, הוא שולח הודעה לדף כדי להציג הודעה קופצת עם הכיתוב 'מוכן לעבודה במצב אופליין', כדי להודיע למשתמש על התכונה:

צילום מסך של הפונקציונליות 'מוכנה לעבודה אופליין' באפליקציית האינטרנט של Squoosh.
ב-Squoosh PWA, קובץ השירות משדר עדכון לדף כשהמטמון מוכן, ובדף מוצג ההודעה 'מוכן לעבודה במצב אופליין'.

שימוש בתיבת העבודה

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

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 ולהטמיע בורר הודעות כדי לקבל הודעות.

כדי להודיע לדף כשמתקין שירות עבודה חדש, משתמשים בקוד הבא:

// 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.

כדי לשלוח הודעה לכרטיסייה האחרונה שבה התמקדו, משתמשים בקוד הבא של עובד השירות:

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

ערוץ הודעות

בשביל Message Channel צריך לבצע שלב הגדרה ראשוני, באמצעות העברת יציאה מהדף ל-Service Worker, כדי ליצור ערוץ תקשורת ביניהם. הדף יוצר מופע של אובייקט MessageChannel ומעביר יציאה לקובץ השירות דרך הממשק 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
};

קובץ השירות מקבל את היציאה ושומר את ההפניה אליה:

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

דפוסים נוספים של תקשורת בין חלון לבין עובד שירות זמינים במאמרים הבאים:

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