יכול להיות שחלק מהאתרים יצטרכו להודיע ל-Service Worker בלי לקבל הודעה על התוצאה. הנה כמה דוגמאות:
- דף שולח לקובץ השירות (service worker) רשימה של כתובות URL לטעינה מראש, כך שכשהמשתמש לוחץ על קישור, משאבי המשנה של המסמך או הדף כבר זמינים במטמון, והניווט הבא מהיר הרבה יותר.
- בדף יש בקשה מ-Service Worker לאחזר ולשמור קבוצה של מאמרים מובילים במטמון, כדי שהם יהיו זמינים למטרות אופליין.
הענקת סוגים כאלה של משימות לא קריטיות ל-service worker מאפשרת לפנות את החוט הראשי כדי לטפל טוב יותר במשימות דחופות יותר, כמו תגובה לאינטראקציות של משתמשים.
במדריך הזה נסביר איך להטמיע טכניקת תקשורת חד-כיוונית מהדף ל-service worker באמצעות ממשקי API רגילים לדפדפנים וספריית Workbox. אנחנו מכנים תרחישים לדוגמה כאלה כשמירה חיונית במטמון.
סביבת ייצור
1-800-Flowers.com הטמיע שמירה במטמון (שליפה מראש) עם קובצי שירות דרך postMessage()
כדי לאחזר מראש את הפריטים המובילים בדפי הקטגוריות, ולזרז את הניווט אל דפים של פרטי מוצרים.
הם משתמשים בגישה משולבת כדי לקבוע אילו פריטים יטענו מראש:
- בזמן טעינת הדף, הוא מבקש מה-service worker לאחזר את נתוני ה-JSON של 9 הפריטים המובילים, ולהוסיף למטמון את האובייקטים של התגובה שנוצרו.
- לגבי הפריטים הנותרים, הם מקשיבים לאירוע
mouseover
, כך שכאשר משתמש מזיז את הסמן מעל פריט, הוא יכול להפעיל אחזור של המשאב על 'פי דרישה'.
הם משתמשים ב-Cache API כדי לאחסן תשובות JSON:
כשמשתמש לוחץ על פריט, אפשר לאסוף מהמטמון את נתוני ה-JSON שמשויכים אליו, בלי צורך לגשת לרשת, וכך לנווט מהר יותר.
שימוש בתיבת העבודה
Workbox מספק דרך קלה לשלוח הודעות לקובץ שירות (service worker), באמצעות החבילה workbox-window
, קבוצה של מודולים שנועדו לפעול בהקשר של החלון. הם משלימים את החבילות האחרות של Workbox שרצות ב-Service Worker.
כדי לתקשר בין הדף לבין ה-service worker, קודם צריך לקבל הפניה לאובייקט Workbox של ה-service worker הרשום:
const wb = new Workbox('/sw.js');
wb.register();
לאחר מכן תוכלו לשלוח את ההודעה באופן דקלרטיבי, בלי הטרחה של קבלת ההרשמה, בדיקת ההפעלה או החשיבה על ממשק ה-API הבסיסי לתקשורת:
wb.messageSW({"type": "PREFETCH", "payload": {"urls": ["/data1.json", "data2.json"]}}); });
ה-Service Worker מיישם handler של message
כדי להאזין להודעות האלה. אפשר להחזיר תשובה, אבל במקרים כאלה זה לא הכרחי:
self.addEventListener('message', (event) => {
if (event.data && event.data.type === 'PREFETCH') {
// do something
}
});
שימוש בממשקי API של דפדפנים
אם ספריית Workbox לא מספיקה לצרכים שלכם, תוכלו להטמיע תקשורת בין חלון לבין שירות עובד באמצעות ממשקי API של דפדפנים.
ניתן להשתמש ב-postMessage API כדי ליצור מנגנון תקשורת חד-כיווני מהדף אל ה-Service Worker.
הדף קורא ל-postMessage()
בממשק של קובץ השירות:
navigator.serviceWorker.controller.postMessage({
type: 'MSG_ID',
payload: 'some data to perform the task',
});
שירות ה-worker מטמיע טיפולן של ההודעות האלה באמצעות message
.
self.addEventListener('message', (event) => {
if (event.data && event.data.type === MSG_ID) {
// do something
}
});
מאפיין {type : 'MSG_ID'}
לא נדרש באופן מוחלט, אבל זו אחת הדרכים לאפשר לדף לשלוח לסוכנות השירות סוגים שונים של הוראות (כלומר, 'לטעינה מראש' לעומת 'לניקוי האחסון'). על סמך הדגל הזה, ה-service worker יכול להסתעף לנתיבי ביצוע שונים.
אם הפעולה בוצעה בהצלחה, המשתמש יוכל ליהנות מהיתרונות שלה, אבל אם לא, היא לא תשנה את התהליך הראשי של המשתמש. לדוגמה, כש-1-800-Flowers.com מנסה לבצע אחסון במטמון מראש, הדף לא צריך לדעת אם שירות ה-worker הצליח. אם כן, המשתמש ייהנה מניווט מהיר יותר. אם לא, עדיין צריך לנווט לדף החדש. זה רק ייקח קצת יותר זמן.
דוגמה פשוטה לשליפה מראש (prefetch)
אחת מהשימושים הנפוצים ביותר של אחסון במטמון באופן גורף היא אחזור מראש, כלומר אחזור משאבים של כתובת URL מסוימת לפני שהמשתמש עובר אליה, כדי לזרז את הניווט.
יש דרכים שונות להטמעת שליפה מראש באתרים:
- שימוש בתגי prefetch של קישורים בדפים: המשאבים נשמרים במטמון הדפדפן למשך חמש דקות, ולאחר מכן חלים הכללים הרגילים של
Cache-Control
לגבי המשאב. - השלמת השיטה הקודמת באמצעות אסטרטגיית מטמון בסביבת זמן הריצה ב-service worker כדי להאריך את משך החיים של המשאב של האחזור מראש מעבר למגבלה הזו.
בתרחישים פשוטים יחסית של אחסון נתונים מראש, כמו אחסון מראש של מסמכים או נכסים ספציפיים (JS, CSS וכו'), השיטות האלה הן הגישה הטובה ביותר.
אם נדרשת לוגיקה נוספת, כמו ניתוח משאב השליפה מראש (קובץ או דף של JSON) כדי לאחזר את כתובות ה-URL הפנימיות שלו, עדיף לתת את המשימה הזו לגמרי ל-Service Worker.
להענקת הגישה לסוגי הפעולות האלה ל-service worker יש את היתרונות הבאים:
- העברת העבודה הקשה של אחזור ועיבוד לאחר אחזור (שנתארח בהמשך) לשרשור משני. כך אפשר לפנות את השרשור הראשי לטיפול במשימות חשובות יותר, כמו תגובה לאינטראקציות של משתמשים.
- מאפשרים לכמה לקוחות (למשל כרטיסיות) לעשות שימוש חוזר בפונקציה משותפת, ואפילו להפעיל את השירות בו-זמנית בלי לחסום את השרשור הראשי.
אחזור מראש של דפים של פרטי מוצרים
קודם כול משתמשים ב-postMessage()
בממשק של Service Worker ומעבירים מערך של כתובות URL למטמון:
navigator.serviceWorker.controller.postMessage({
type: 'PREFETCH',
payload: {
urls: [
'www.exmaple.com/apis/data_1.json',
'www.exmaple.com/apis/data_2.json',
],
},
});
בקובץ ה-service worker, מטמיעים טיפולן של הודעות message
כדי ליירט ולעבד הודעות שנשלחות מכל כרטיסייה פעילה:
addEventListener('message', (event) => {
let data = event.data;
if (data && data.type === 'PREFETCH') {
let urls = data.payload.urls;
for (let i in urls) {
fetchAsync(urls[i]);
}
}
});
בקוד הקודם הוספנו פונקציית עזר קטנה בשם fetchAsync()
כדי להריץ חזרה על מערך כתובות ה-URL ולשלוח בקשת אחזור לכל אחת מהן:
async function fetchAsync(url) {
// await response of fetch call
let prefetched = await fetch(url);
// (optionally) cache resources in the service worker storage
}
כשהתגובה מתקבלת, אפשר להסתמך על כותרות האחסון במטמון של המשאב. עם זאת, במקרים רבים, כמו בדפי פרטי מוצרים, המשאבים לא נשמרים במטמון (כלומר, יש להם כותרת Cache-control
של no-cache
). במקרים כאלה, אפשר לשנות את ההתנהגות הזו על ידי אחסון המשאב שאוחזר במטמון של ה-service worker. היתרון הנוסף של האפשרות הזו הוא שאפשר להציג את הקובץ בתרחישים אופליין.
מעבר לנתוני JSON
אחרי שנתוני ה-JSON מאוחזרים מנקודת הקצה של השרת, בדרך כלל הם מכילים כתובות URL אחרות שגם הן כדאיות לשליפה מראש, כמו תמונה או נתונים אחרים של נקודת הקצה שמשויכים לנתונים האלה ברמה הראשונה.
נניח שבדוגמה שלנו, נתוני ה-JSON שהוחזרו הם המידע של אתר לקניות מכולת:
{
"productName": "banana",
"productPic": "https://cdn.example.com/product_images/banana.jpeg",
"unitPrice": "1.99"
}
משנים את הקוד של fetchAsync()
כדי להריץ חזרה על רשימת המוצרים ולשמור את תמונת ה-hero של כל אחד מהם במטמון:
async function fetchAsync(url, postProcess) {
// await response of fetch call
let prefetched = await fetch(url);
//(optionally) cache resource in the service worker cache
// carry out the post fetch process if supplied
if (postProcess) {
await postProcess(prefetched);
}
}
async function postProcess(prefetched) {
let productJson = await prefetched.json();
if (productJson && productJson.product_pic) {
fetchAsync(productJson.product_pic);
}
}
אפשר להוסיף טיפול בחריגות לקוד הזה למקרים כמו שגיאות 404. אבל היתרון של שימוש בקובץ שירות (service worker) כדי לבצע אחסון מקדים הוא שהוא יכול להיכשל בלי להשפיע באופן משמעותי על הדף ועל השרשור הראשי. אפשר גם להשתמש בלוגיקה מתוחכמת יותר בעיבוד של התוכן שנטען מראש, כדי להפוך אותו לגמיש יותר ולנתק אותו מהנתונים שהוא מטפל בהם. השמיים הם הגבול.
סיכום
במאמר הזה התייחסנו לתרחיש לדוגמה נפוץ של תקשורת חד-כיוונית בין דף לבין עובד שירות: אחסון במטמון של הוראות. הדוגמאות שצוינו נועדו להדגים דרך אחת לשימוש בדפוס הזה בלבד, וניתן ליישם את אותה גישה גם במקרי שימוש אחרים, לדוגמה, שמירה במטמון של כתבות מובילות לפי דרישה לצורך שימוש אופליין, הוספה לסימניות ועוד.
דפוסים נוספים של תקשורת בין דפים לבין שירותי עבודה:
- עדכוני שידור: שליחת קריאה לדף מ-Service Worker כדי לספק מידע על עדכונים חשובים (למשל, יש גרסה חדשה של אפליקציית האינטרנט).
- תקשורת דו-כיוונית: הענקת גישה למשימה לעובד שירות (למשל, הורדה כבדה) ועדכון הדף לגבי ההתקדמות.