השרת ממלא את הבקשה

אחד מההיבטים העיקריים של Progressive Web Apps הוא שהן אמינות. הם יכולים לטעון נכסים במהירות, לשמור על רמת העניין של המשתמשים ולתת משוב באופן מיידי, גם בתנאי רשת גרועים. איך זה אפשרי? תודה לאירוע fetch של קובץ שירות (service worker).

אירוע האחזור

תמיכה בדפדפן

  • Chrome: 40.
  • קצה: 17.
  • Firefox: 44.
  • Safari: 11.1.

מקור

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

ה-handler של fetch מקבל את כל הבקשות מאפליקציה, כולל כתובות URL וכותרות HTTP, ומאפשר למפתח האפליקציה להחליט איך לעבד אותן.

קובץ השירות (service worker) נמצא בין הלקוח לרשת.

ה-Service Worker יכול להעביר בקשה לרשת, להגיב באמצעות תגובה שנשמרה בעבר במטמון או ליצור תשובה חדשה. הבחירה היא שלך. דוגמה פשוטה:

self.addEventListener("fetch", event => {
    console.log(`URL requested: ${event.request.url}`);
});

שליחת תגובה לבקשה

יש שני דברים שאפשר לעשות כשבקשה מגיעה ל-Service Worker: אפשר להתעלם ממנה, לאפשר לה לעבור לרשת, או להגיב לה. שליחת תגובות לבקשות מתוך Service Worker מאפשרת לכם לבחור מה ואיך הוא מוחזר ל-PWA, גם כשהמשתמש במצב אופליין.

כדי להגיב לבקשה נכנסת, צריך לבצע קריאה אל event.respondWith() מתוך הגורם המטפל באירועים של fetch, באופן הבא:

// fetch event handler in your service worker file
self.addEventListener("fetch", event => {
    const response = .... // a response or a Promise of response
    event.respondWith(response);
});

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

יצירת תשובות

בעזרת Fetch API, ניתן ליצור תגובות HTTP בקוד ה-JavaScript, ואת התגובות האלה אפשר לשמור במטמון באמצעות Cache Storage API ולהחזיר אותן כאילו הן הגיעו משרת אינטרנט.

כדי ליצור תשובה, יוצרים אובייקט Response חדש, מגדירים את הגוף ואפשרויות כמו סטטוס וכותרות:

const simpleResponse = new Response("Body of the HTTP response");

const options = {
   status: 200,
   headers: {
    'Content-type': 'text/html'
   }
};
const htmlResponse = new Response("<b>HTML</b> content", options)

תשובות מהמטמון

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

אפשר להשתמש ב-API של אחסון המטמון כדי לבדוק אם הבקשה שהתקבלה מה-PWA זמינה במטמון. אם היא זמינה, אפשר להשיב באמצעותה ל-respondWith(). כדי לעשות זאת, קודם צריך לחפש במטמון. הפונקציה match(), שזמינה בממשק caches ברמה העליונה, מחפשת בכל החנויות במקור או באובייקט אחד של מטמון פתוח.

הפונקציה match() מקבלת בקשת HTTP או כתובת URL כארגומנט, ומחזירה הבטחה שמסתיימת בתשובה שמשויכת למפתח המתאים.

// Global search on all caches in the current origin
caches.match(urlOrRequest).then(response => {
   console.log(response ? response : "It's not in the cache");
});

// Cache-specific search
caches.open("pwa-assets").then(cache => {
  cache.match(urlOrRequest).then(response => {
    console.log(response ? response : "It's not in the cache");
  });
});

אסטרטגיות של שמירה במטמון

הצגת קבצים רק מהמטמון של הדפדפן לא מתאימה לכל תרחיש לדוגמה. לדוגמה, המשתמש או הדפדפן יכולים להוציא את המטמון. לכן כדאי לכם להגדיר אסטרטגיות משלכם להעברת נכסים לאפליקציית ה-PWA. אינך מוגבל לאסטרטגיית שמירה אחת במטמון. אפשר להגדיר כתובות URL שונות לתבניות שונות של כתובות URL. לדוגמה, אפשר ליצור שיטה אחת לכמה שיותר נכסים בממשק המשתמש, ואסטרטגיה אחת לקריאות ל-API ואסטרטגיה נוספת לכתובות URL של תמונות ונתונים. כדי לעשות זאת, צריך לקרוא את event.request.url ב-ServiceWorkerGlobalScope.onfetch ולנתח אותו באמצעות ביטויים רגולריים או דפוס כתובת URL. (נכון למועד הכתיבה, תבנית כתובת אתר אינה נתמכת בכל הפלטפורמות).

האסטרטגיות הנפוצות ביותר הן:

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

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

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

האסטרטגיה &#39;מטמון ראשון&#39;

self.addEventListener("fetch", event => {
   event.respondWith(
     caches.match(event.request)
     .then(cachedResponse => {
       // It can update the cache to serve updated content on the next request
         return cachedResponse || fetch(event.request);
     }
   )
  )
});

קודם רשת

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

האסטרטגיה של Network First

self.addEventListener("fetch", event => {
   event.respondWith(
     fetch(event.request)
     .catch(error => {
       return caches.match(event.request) ;
     })
   );
});

לא פעיל במהלך אימות מחדש

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

לא פעיל במהלך אימות מחדש של אסטרטגיה

self.addEventListener('fetch', event => {
  event.respondWith(
    caches.match(event.request).then(cachedResponse => {
        const networkFetch = fetch(event.request).then(response => {
          // update the cache with a clone of the network response
          const responseClone = response.clone()
          caches.open(url.searchParams.get('name')).then(cache => {
            cache.put(event.request, responseClone)
          })
          return response
        }).catch(function (reason) {
          console.error('ServiceWorker fetch failed: ', reason)
        })
        // prioritize cached response over network
        return cachedResponse || networkFetch
      }
    )
  )
})

רשת בלבד

האסטרטגיה מסוג 'רשת בלבד' דומה לאופן הפעולה של דפדפנים ללא Service Worker או Cache Storage API. הבקשות יחזירו משאב רק אם ניתן לאחזר אותו מהרשת. בדרך כלל האפשרות הזו שימושית למשאבים כמו בקשות API אונליין בלבד.

האסטרטגיה של &#39;רשת בלבד&#39;

מטמון בלבד

האסטרטגיה של 'מטמון בלבד' מבטיחה שהבקשות אף פעם לא יישלחו לרשת. כל הבקשות הנכנסות נענות באמצעות פריט מטמון שאוכלס מראש. הקוד הבא משתמש ב-handler של האירוע fetch עם השיטה match של אחסון המטמון כדי להגיב רק למטמון:

self.addEventListener("fetch", event => {
   event.respondWith(caches.match(event.request));
});

אסטרטגיית &#39;מטמון בלבד&#39;.

שיטות מותאמות אישית

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

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

מתבצע עדכון של הנכסים

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

משאבים