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

שימוש ב-Workbox
workbox-window
הוא קבוצה של מודולים של ספריית Workbox שמיועדים להפעלה בהקשר של חלון. המחלקות Workbox
מספקות שיטת messageSW()
לשליחת הודעה ל-service worker הרשום של המופע ולהמתנה לתשובה.
קוד הדף הבא יוצר מופע חדש של Workbox
ושולח הודעה ל-service worker כדי לקבל את הגרסה שלו:
const wb = new Workbox('/sw.js');
wb.register();
const swVersion = await wb.messageSW({type: 'GET_VERSION'});
console.log('Service Worker version:', swVersion);
ה-service worker מטמיע מאזין להודעות בקצה השני, ומגיב ל-service worker הרשום:
const SW_VERSION = '1.0.0';
self.addEventListener('message', (event) => {
if (event.data.type === 'GET_VERSION') {
event.ports[0].postMessage(SW_VERSION);
}
});
מתחת לפני השטח, הספרייה משתמשת ב-API של דפדפן שנסקור בקטע הבא: Message Channel. אבל היא מסתירה פרטים רבים של ההטמעה, מה שמקל על השימוש בה, תוך ניצול התמיכה הרחבה בדפדפנים שיש ל-API הזה.

שימוש בממשקי API של דפדפן
אם ספריית Workbox לא מספיקה לצרכים שלכם, יש כמה ממשקי API ברמה נמוכה יותר שזמינים להטמעה של תקשורת דו-כיוונית בין דפים לבין Service Workers. יש כמה נקודות דמיון והבדלים בין שני סוגי המודעות:
תכונות דומות:
- בכל המקרים, התקשורת מתחילה בצד אחד דרך ממשק
postMessage()
ומתקבלת בצד השני על ידי הטמעה של handlermessage
. - בפועל, כל ממשקי ה-API הזמינים מאפשרים לנו להטמיע את אותם תרחישי שימוש, אבל חלק מהם עשויים לפשט את הפיתוח בתרחישים מסוימים.
ההבדלים:
- יש להם דרכים שונות לזהות את הצד השני של התקשורת: חלקם משתמשים בהפניה מפורשת להקשר השני, בעוד שאחרים יכולים לתקשר באופן מרומז באמצעות אובייקט proxy שמופעל בכל צד.
- התמיכה בדפדפנים משתנה בין האפליקציות.

Broadcast Channel API
Broadcast Channel API מאפשר תקשורת בסיסית בין הקשרים של גלישה באמצעות אובייקטים של BroadcastChannel.
כדי להטמיע את הפתרון הזה, קודם כל צריך ליצור מופע של אובייקט BroadcastChannel
בכל הקשר עם אותו מזהה, ולשלוח ולקבל ממנו הודעות:
const broadcast = new BroadcastChannel('channel-123');
האובייקט BroadcastChannel חושף ממשק postMessage()
לשליחת הודעה לכל הקשרים שמקשיבים:
//send message
broadcast.postMessage({ type: 'MSG_ID', });
כל הקשר של דפדפן יכול להאזין להודעות באמצעות השיטה onmessage
של האובייקט BroadcastChannel
:
//listen to messages
broadcast.onmessage = (event) => {
if (event.data && event.data.type === 'MSG_ID') {
//process message...
}
};
כפי שאפשר לראות, אין הפניה מפורשת להקשר מסוים, ולכן אין צורך לקבל הפניה קודם ל-service worker או ללקוח מסוים.

החיסרון הוא שבזמן כתיבת המאמר הזה, ה-API נתמך על ידי Chrome, Firefox ו-Edge, אבל דפדפנים אחרים, כמו Safari, עדיין לא תומכים בו.
Client API
Client API מאפשר לכם לקבל הפניה לכל האובייקטים של WindowClient
שמייצגים את הכרטיסיות הפעילות ש-Service Worker שולט בהן.
מכיוון שהדף נשלט על ידי קובץ שירות יחיד, הוא מאזין להודעות ושולח אותן ישירות לקובץ השירות הפעיל דרך הממשק serviceWorker
:
//send message
navigator.serviceWorker.controller.postMessage({
type: 'MSG_ID',
});
//listen to messages
navigator.serviceWorker.onmessage = (event) => {
if (event.data && event.data.type === 'MSG_ID') {
//process response
}
};
באופן דומה, ה-service worker מאזין להודעות באמצעות הטמעה של onmessage
listener:
//listen to messages
self.addEventListener('message', (event) => {
if (event.data && event.data.type === 'MSG_ID') {
//Process message
}
});
כדי לתקשר עם אחד מהלקוחות שלו, ה-service worker מקבל מערך של אובייקטים מסוג WindowClient
על ידי הפעלת שיטות כמו Clients.matchAll()
ו-Clients.get()
. לאחר מכן, הוא יכול postMessage()
כל אחד מהם:
//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'});
}
});

Client API
היא אפשרות טובה לתקשר בקלות עם כל הכרטיסיות הפעילות מ-service worker
בצורה פשוטה יחסית. API נתמך על ידי כל הדפדפנים העיקריים, אבל יכול להיות שלא כל השיטות שלו יהיו זמינות. לכן, חשוב לבדוק את התמיכה בדפדפן לפני שמטמיעים אותו באתר.
ערוץ הודעות
ערוץ הודעות מחייב הגדרה והעברה של יציאה מהקשר אחד להקשר אחר כדי ליצור ערוץ תקשורת דו-כיווני.
כדי לאתחל את הערוץ, הדף יוצר מופע של אובייקט MessageChannel
ומשתמש בו כדי לשלוח יציאה ל-service worker הרשום. בדף מיושמת גם פונקציית onmessage
listener כדי לקבל הודעות מההקשר השני:
const messageChannel = new MessageChannel();
//Init port
navigator.serviceWorker.controller.postMessage({type: 'PORT_INITIALIZATION'}, [
messageChannel.port2,
]);
//Listen to messages
messageChannel.port1.onmessage = (event) => {
// Process message
};

ה-service worker מקבל את היציאה, שומר הפניה אליה ומשתמש בה כדי לשלוח הודעה לצד השני:
let communicationPort;
//Save reference to port
self.addEventListener('message', (event) => {
if (event.data && event.data.type === 'PORT_INITIALIZATION') {
communicationPort = event.ports[0];
}
});
//Send messages
communicationPort.postMessage({type: 'MSG_ID'});
MessageChannel
נתמך כרגע על ידי כל הדפדפנים העיקריים.
ממשקי API מתקדמים: סנכרון ברקע ואחזור ברקע
במדריך הזה הסברנו איך להטמיע טכניקות תקשורת דו-כיוונית במקרים פשוטים יחסית, כמו העברת הודעת מחרוזת שמתארת את הפעולה שצריך לבצע, או רשימה של כתובות URL שצריך לשמור במטמון מהקשר אחד להקשר אחר. בקטע הזה נסביר על שני ממשקי API לטיפול בתרחישים ספציפיים: חוסר קישוריות והורדות ארוכות.
סנכרון ברקע
יכול להיות שאפליקציית צ'אט תרצה לוודא שהודעות לא יאבדו בגלל חיבור לא יציב. Background Sync API מאפשר לדחות פעולות כדי לנסות אותן שוב כשהמשתמש מחובר לרשת יציבה. האפשרות הזו שימושית כדי לוודא שכל מה שהמשתמש רוצה לשלוח אכן נשלח.
במקום הממשק postMessage()
, הדף רושם sync
:
navigator.serviceWorker.ready.then(function (swRegistration) {
return swRegistration.sync.register('myFirstSync');
});
לאחר מכן, ה-service worker מאזין לאירוע sync
כדי לעבד את ההודעה:
self.addEventListener('sync', function (event) {
if (event.tag == 'myFirstSync') {
event.waitUntil(doSomeStuff());
}
});
הפונקציה doSomeStuff()
צריכה להחזיר הבטחה שמציינת אם הפעולה שהיא מנסה לבצע הצליחה או נכשלה. אם ההבטחה מתקיימת, הסנכרון הושלם. אם הסנכרון ייכשל, יתוזמן סנכרון נוסף לניסיון חוזר. גם ניסיונות חוזרים של סנכרון מחכים לקישוריות, ומשתמשים בהשהיה מעריכית לפני ניסיון חוזר.
אחרי שהפעולה מתבצעת, ה-service worker יכול לתקשר עם הדף כדי לעדכן את ממשק המשתמש, באמצעות אחד מ-APIs התקשורת שפורטו קודם.
חיפוש Google משתמש בסנכרון ברקע כדי לשמור שאילתות שנכשלו בגלל חיבור לא טוב, ולנסות שוב לבצע אותן מאוחר יותר כשהמשתמש מחובר לאינטרנט. אחרי שהפעולה מתבצעת, התוצאה מועברת למשתמש באמצעות התראה בדפדפן:

אחזור ברקע
אם מדובר במשימות קצרות יחסית, כמו שליחת הודעה או רשימה של כתובות URL שצריך לשמור במטמון, האפשרויות שראינו עד עכשיו הן בחירה טובה. אם המשימה אורכת יותר מדי זמן, הדפדפן יסגור את service worker, אחרת יש סיכון לפרטיות המשתמש ולסוללה.
ה-API של Background Fetch מאפשר להעביר משימה ארוכה ל-service worker, כמו הורדה של סרטים, פודקאסטים או רמות במשחק.
כדי לתקשר עם קובץ השירות מהדף, משתמשים ב-backgroundFetch.fetch
במקום ב-postMessage()
:
navigator.serviceWorker.ready.then(async (swReg) => {
const bgFetch = await swReg.backgroundFetch.fetch(
'my-fetch',
['/ep-5.mp3', 'ep-5-artwork.jpg'],
{
title: 'Episode 5: Interesting things.',
icons: [
{
sizes: '300x300',
src: '/ep-5-icon.png',
type: 'image/png',
},
],
downloadTotal: 60 * 1024 * 1024,
},
);
});
האובייקט BackgroundFetchRegistration
מאפשר לדף להאזין לאירוע progress
כדי לעקוב אחרי התקדמות ההורדה:
bgFetch.addEventListener('progress', () => {
// If we didn't provide a total, we can't provide a %.
if (!bgFetch.downloadTotal) return;
const percent = Math.round(
(bgFetch.downloaded / bgFetch.downloadTotal) * 100,
);
console.log(`Download progress: ${percent}%`);
});

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