מדריך ציווי לשמירה במטמון

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

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

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

האצלת הסוגים האלה של משימות לא קריטיות ל-Service Worker מאפשרת שחרור של ה-thread הראשי, כדי לטפל טוב יותר במשימות דחופות יותר, כמו תגובה לאינטראקציות של משתמשים.

תרשים של דף שמבקש לשמור משאבים במטמון של Service Worker.

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

מארז ייצור

1-800-Flowers.com הטמיעה שמירה במטמון (prefetch) של התכונה 'שמירה מראש במטמון' (prefetch) עם קובצי שירות (service worker) באמצעות postMessage(), כדי לאחזר מראש את הפריטים המובילים בדפי הקטגוריות, וכך לזרז את הניווט הבא לדפים של פרטי המוצרים.

לוגו של 1-800 פרחים.

הם משתמשים בגישה מעורבת כדי להחליט אילו פריטים לשליפה מראש (prefetch):

  • בזמן טעינת הדף הוא מבקש מה-servicer worker לאחזר את נתוני ה-JSON של 9 הפריטים המובילים, ולהוסיף למטמון את אובייקטי התגובה שהתקבלו.
  • לגבי הפריטים הנותרים, הם מקשיבים לאירוע mouseover , כך שכאשר משתמש מעביר את הסמן מעל הפריט, הוא יכול להפעיל אחזור של המשאב ב-'demand'.

הם משתמשים ב-Cache API כדי לשמור תגובות JSON:

לוגו של 1-800 פרחים.
שליפה מראש (prefetch) של נתוני מוצרים בפורמט JSON מדפי כרטיסי מוצר ב-1-800Flowers.com.

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

אפשר להשתמש ב-postMessage API על מנת ליצור מנגנון תקשורת חד-כיווני מהדף אל ה-Service Worker.

הדף קורא ל-postMessage() בממשק של קובץ השירות (service worker):

navigator.serviceWorker.controller.postMessage({
  type: 'MSG_ID',
  payload: 'some data to perform the task',
});

כדי להאזין להודעות האלה, ה-Service Worker מטמיע handler של message.

self.addEventListener('message', (event) => {
  if (event.data && event.data.type === MSG_ID) {
    // do something
  }
});

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

אם הפעולה הצליחה, המשתמש יוכל ליהנות מהיתרונות שלה, אך אם לא, היא לא תשנה את זרימת המשתמש הראשית. לדוגמה, כאשר 1-800-Flowers.com מנסה לשמור מראש קובץ שמור, הדף לא צריך לדעת אם ה-Service Worker הצליח. אם כן, המשתמש ייהנה מניווט מהיר יותר. אם לא, צריך לעבור לדף החדש בכל זאת. זה ייקח רק עוד קצת זמן.

דוגמה פשוטה לאחזור מראש

אחת מהאפליקציות הנפוצות ביותר של שמירה במטמון היא שליפה מראש (prefetch), כלומר אחזור משאבים לכתובת URL נתונה, לפני שהמשתמש עובר אליה, כדי לזרז את הניווט.

יש כמה דרכים להטמיע שליפה מראש באתרים:

בתרחישים פשוטים יחסית של שליפה מראש, כמו שליפה מראש (prefetch) של מסמכים או נכסים ספציפיים (JS, CSS וכו'), השיטות האלה הן הגישה הטובה ביותר.

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

האצלת סוגי פעולות אלה ל-Service Worker כוללת את היתרונות הבאים:

  • עומס העבודה הקשה של אחזור ועיבוד לאחר השליפה (שיושק מאוחר יותר) ב-thread משני. כך ה-thread הראשי פנוי לטפל במשימות חשובות יותר, כמו תגובה לאינטראקציות של משתמשים.
  • מתן אפשרות למספר לקוחות (למשל כרטיסיות) לעשות שימוש חוזר בפונקציונליות נפוצה, ואפילו לבצע קריאה לשירות בו-זמנית בלי לחסום את ה-thread הראשי.

שליפה מראש של דפים של פרטי מוצרים

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

סיכום

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

דפוסי תקשורת נוספים של הדף ושל Service Worker מפורטים ב:

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