שימוש ביישומי פלאגין

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

ב-Workbox יש כמה פלאגינים מוכנים לשימוש, ואפשר גם לכתוב פלאגינים מותאמים אישית בהתאם לדרישות של האפליקציה.

יישומי פלאגין זמינים לתיבת העבודה

ב-Workbox יש את הפלאגינים הרשמיים הבאים לשימוש ב-service worker:

כדי להשתמש בפלאגינים של Workbox – בין אם מדובר באחד מהפלאגינים שצוינו למעלה ובין אם מדובר בפלאגין בהתאמה אישית – עם אסטרטגיית Workbox, מוסיפים מופע של הפלאגין למאפיין plugins של האסטרטגיה:

import {registerRoute} from 'workbox-routing';
import {CacheFirst} from 'workbox-strategies';
import {ExpirationPlugin} from 'workbox-expiration';

registerRoute(
  ({request}) => request.destination === 'image',
  new CacheFirst({
    cacheName: 'images',
    plugins: [
      new ExpirationPlugin({
        maxEntries: 60,
        maxAgeSeconds: 30 * 24 * 60 * 60, // 30 Days
      }),
    ],
  })
);

שיטות לפלאגינים מותאמים אישית

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

  • cacheWillUpdate: הקריאה מתבצעת לפני שמשתמשים ב-Response כדי לעדכן מטמון. בשיטה הזו, אפשר לשנות את התגובה לפני שהיא תתווסף למטמון, או להחזיר null כדי להימנע מעדכון מלא של המטמון.
  • cacheDidUpdate: מתבצעת קריאה כשרשומה חדשה מתווספת למטמון או כשרשומה קיימת מתעדכנת. פלאגינים שמשתמשים בשיטה הזו יכולים להיות שימושיים כשרוצים לבצע פעולה אחרי עדכון של המטמון.
  • cacheKeyWillBeUsed: הקריאה מתבצעת לפני שמשתמשים בבקשה כמפתח מטמון. הקריאה החוזרת הזו מתבצעת גם בחיפושים במטמון (כשהערך של mode הוא 'read') וגם בכתיבת נתונים במטמון (כשהערך של mode הוא 'write'). הקריאה החוזרת הזו שימושית אם צריך לשנות או לתקן כתובות URL לפני שמשתמשים בהן כדי לגשת למטמון.
  • cachedResponseWillBeUsed: מתבצעת קריאה לפני השימוש בתגובה מהמטמון, כדי לבדוק את התגובה. בשלב הזה אפשר להחזיר תשובה אחרת או להחזיר null.
  • requestWillFetch: תישלח בקשה בכל פעם שבקשה עומדת לעבור לרשת. שימושי אם צריך לשנות את ה-Request ממש לפני שהוא עובר לרשת.
  • fetchDidFail: האירוע הזה מופעל כשבקשת רשת נכשלת, כנראה בגלל חוסר קישוריות לרשת. הוא לא יופעל כשלדפדפן יש חיבור לרשת, אבל הוא מקבל הודעת שגיאה (לדוגמה, 404 Not Found).
  • fetchDidSucceed: מתבצעת קריאה בכל פעם שבקשת רשת מצליחה, ללא קשר לקוד תגובת ה-HTTP.
  • handlerWillStart: בוצעה קריאה לפני שלוגיקה של handler כלשהי מתחילה לפעול. האפשרות הזו שימושית אם צריך להגדיר את המצב הראשוני של ה-handler. לדוגמה, אם רוצים לדעת כמה זמן חלף עד שהטיפול יצר תשובה, אפשר לתעד את שעת ההתחלה בקריאה החוזרת.
  • handlerWillRespond: נשלחת קריאה לפני השיטה handle() של השיטה שמחזירה תשובה. זו אפשרות שימושית אם צריך לשנות את התשובה לפני החזרתה ל-RouteHandler או ללוגיקה מותאמת אישית אחרת.
  • handlerDidRespond: הקריאה מתבצעת אחרי שהשיטה handle() של האסטרטגיה מחזירה תשובה. זה הזמן המתאים לתעד את פרטי התגובה הסופיים (לדוגמה, אחרי שינויים שבוצעו על ידי יישומי פלאגין אחרים).
  • handlerDidComplete: הקריאה מתבצעת אחרי שהסתיימו כל ההבטחות להארכת משך החיים שנוספו לאירוע מהפעלת האסטרטגיה. האפשרות הזו שימושית אם אתם צריכים לדווח על נתונים שצריך להמתין עד שה-handler יסתיים כדי לחשב דברים כמו סטטוס היט של מטמון, זמן אחזור של מטמון, זמן אחזור של רשת ומידע שימושי אחר.
  • handlerDidError: מתבצעת קריאה אם ה-handler לא יכול לספק תשובה תקינה ממקור כלשהו. זו השעה האופטימלית לספק סוג כלשהו של תשובה חלופית כחלופה לכשל מידית.

כל הקריאות החוזרות האלה הן async, ולכן יהיה צורך להשתמש ב-await בכל פעם שאירוע מטמון או אחזור יגיעו לנקודה הרלוונטית של הקריאה החוזרת הרלוונטית.

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

const myPlugin = {
  cacheWillUpdate: async ({request, response, event, state}) => {
    // Return `response`, a different `Response` object, or `null`.
    return response;
  },
  cacheDidUpdate: async ({
    cacheName,
    request,
    oldResponse,
    newResponse,
    event,
    state,
  }) => {
    // No return expected
    // Note: `newResponse.bodyUsed` is `true` when this is called,
    // meaning the body has already been read. If you need access to
    // the body of the fresh response, use a technique like:
    // const freshResponse = await caches.match(request, {cacheName});
  },
  cacheKeyWillBeUsed: async ({request, mode, params, event, state}) => {
    // `request` is the `Request` object that would otherwise be used as the cache key.
    // `mode` is either 'read' or 'write'.
    // Return either a string, or a `Request` whose `url` property will be used as the cache key.
    // Returning the original `request` will make this a no-op.
    return request;
  },
  cachedResponseWillBeUsed: async ({
    cacheName,
    request,
    matchOptions,
    cachedResponse,
    event,
    state,
  }) => {
    // Return `cachedResponse`, a different `Response` object, or null.
    return cachedResponse;
  },
  requestWillFetch: async ({request, event, state}) => {
    // Return `request` or a different `Request` object.
    return request;
  },
  fetchDidFail: async ({originalRequest, request, error, event, state}) => {
    // No return expected.
    // Note: `originalRequest` is the browser's request, `request` is the
    // request after being passed through plugins with
    // `requestWillFetch` callbacks, and `error` is the exception that caused
    // the underlying `fetch()` to fail.
  },
  fetchDidSucceed: async ({request, response, event, state}) => {
    // Return `response` to use the network response as-is,
    // or alternatively create and return a new `Response` object.
    return response;
  },
  handlerWillStart: async ({request, event, state}) => {
    // No return expected.
    // Can set initial handler state here.
  },
  handlerWillRespond: async ({request, response, event, state}) => {
    // Return `response` or a different `Response` object.
    return response;
  },
  handlerDidRespond: async ({request, response, event, state}) => {
    // No return expected.
    // Can record final response details here.
  },
  handlerDidComplete: async ({request, response, error, event, state}) => {
    // No return expected.
    // Can report any data here.
  },
  handlerDidError: async ({request, event, error, state}) => {
    // Return a `Response` to use as a fallback, or `null`.
    return fallbackResponse;
  },
};

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

כל הקריאות החוזרות (callback) של יישומי הפלאגין מועברות גם לאובייקט state, שהוא ייחודי לפלאגין מסוים ולאסטרטגיה שהוא מפעיל. המשמעות היא שאפשר לכתוב יישומי פלאגין שבהם פונקציית קריאה חוזרת אחת יכולה לבצע משימה באופן מותנה על סמך מה שפונקציית קריאה חוזרת אחרת באותו פלאגין עשתה (לדוגמה, חישוב ההבדל בין הפעלת requestWillFetch() לבין הפעלת fetchDidSucceed() או fetchDidFail()).

יישומי פלאגין של צד שלישי

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

אתם יכולים לחפש במאגר של npm כדי למצוא עוד יישומי פלאגין של Workbox שסופקו על ידי הקהילה.

לסיום, אם יצרתם פלאגין של Workbox ואתם רוצים לשתף אותו, צריך להוסיף את מילת המפתח workbox-plugin כשמפרסמים אותה. אם כן, נשמח לשמוע מכם ב-Twitter‏ ‎@WorkboxJS.