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

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

אירוע האחזור

Browser Support

  • Chrome: 40.
  • Edge: 17.
  • Firefox: 44.
  • Safari: 11.1.

Source

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

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

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

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

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

מענה לבקשה

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

יצירת תשובות

בעזרת 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 כארגומנט, ומחזירה promise שמתקבלת בו התגובה שמשויכת למפתח המתאים.

// 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 שונות. לדוגמה, אפשר להגדיר אסטרטגיה אחת לנכסי ממשק המשתמש המינימליים, אסטרטגיה אחרת לשיחות API ואסטרטגיה שלישית לכתובות URL של תמונות ונתונים. כדי לעשות זאת, קוראים את event.request.url ב-ServiceWorkerGlobalScope.onfetch ומנתחים אותו באמצעות ביטויים רגולריים או תבנית URL. (נכון למועד כתיבת המאמר, התכונה 'תבנית כתובת URL' לא נתמכת בכל הפלטפורמות).

השיטות הנפוצות ביותר הן:

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

שמירת הנתונים במטמון קודם

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

האסטרטגיה &#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);
     }
   )
  )
});

רשת קודם

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

האסטרטגיה &#39;רשת קודם&#39;

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

Stale בזמן אימות מחדש

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

האסטרטגיה &#39;לא עדכני בזמן אימות מחדש&#39;

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;

מטמון בלבד

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

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

אסטרטגיה של שמירת פריטים במטמון בלבד.

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

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

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

עדכון נכסים

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

משאבים