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

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

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

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

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

תרשים של דף שמבקש משאבים לשמירת מטמון בקובץ שירות (service worker).

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

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

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

הלוגו של 1-800 Flowers.

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

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

הם משתמשים ב-Cache API כדי לאחסן תשובות בפורמט JSON:

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

כשהמשתמש לוחץ על פריט, אפשר לאחזר את נתוני ה-JSON המשויכים אליו מהמטמון, בלי צורך לעבור לרשת, וכך הניווט מהיר יותר.

שימוש ב-Workbox

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"]}}); });

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

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

אחת מהשימושים הנפוצים ביותר של אחסון במטמון באופן גורף היא אחזור מראש, כלומר אחזור משאבים של כתובת URL מסוימת לפני שהמשתמש עובר אליה, כדי לזרז את הניווט.

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

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

סיכום

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

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

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