אחד ההיבטים המרכזיים של אפליקציות אינטרנט מתקדמות הוא שהן אמינות. הן יכולות לטעון נכסים במהירות, לשמור על מעורבות המשתמשים ולספק משוב באופן מיידי, גם בתנאי רשת גרועים. איך זה יכול להיות? הודות לאירוע fetch
של Service Worker.
אירוע האחזור
האירוע fetch
מאפשר לנו ליירט כל בקשה לרשת שמתבצעת על ידי ה-PWA בהיקף של ה-service worker, גם לבקשות מסוג same-origin וגם לבקשות מסוג cross-origin. בנוסף לבקשות ניווט ובקשות לנכסים, אחזור מ-Service Worker מותקן מאפשר להציג ביקורים בדפים אחרי הטעינה הראשונה של האתר בלי לבצע קריאות לרשת.
ה-handler fetch
מקבל את כל הבקשות מאפליקציה, כולל כתובות URL וכותרות HTTP, ומאפשר למפתח האפליקציה להחליט איך לעבד אותן.
קובץ שירות (service worker) יכול להעביר בקשה לרשת, להשיב באמצעות תגובה ששמורה במטמון או ליצור תגובה חדשה. הבחירה היא שלך. לדוגמה:
self.addEventListener("fetch", event => {
console.log(`URL requested: ${event.request.url}`);
});
מענה לבקשה
כשבקשה מגיעה לקובץ שירות (service worker), יש שתי אפשרויות: להתעלם ממנה, ואז היא תועבר לרשת, או להגיב לה. המענה לבקשות מתוך ה-service worker מאפשר לכם לבחור מה יוחזר ל-PWA ואיך, גם כשהמשתמש אופליין.
כדי להגיב לבקשה נכנסת, מתקשרים אל event.respondWith()
מתוך handler של אירוע 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()
אחרי שסיימתם את הטיפול באירוע האחזור, למשל בתוך קריאה אסינכרונית. אם צריך לחכות לתשובה המלאה, אפשר להעביר Promise ל-respondWith()
שמוביל ל-Response.
יצירת תשובות
בעזרת 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, הגיע הזמן להשתמש בממשק Caching Storage כדי לאחסן נכסים במכשיר.
אתם יכולים להשתמש ב-Cache Storage 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
- התגובה מוגשת מהמטמון, ובמקביל מתבצעת ברקע בקשה לגרסה העדכנית והיא נשמרת במטמון לפעם הבאה שבה תתבצע בקשה לנכס.
- רשת בלבד
- תמיד משיב עם תגובה מהרשת או עם שגיאות. המטמון לא נבדק.
- מטמון בלבד
- תמיד משיב עם תגובה מהמטמון או עם שגיאות. המערכת אף פעם לא תבדוק את הרשת. צריך להוסיף למטמון את הנכסים שיוצגו באמצעות האסטרטגיה הזו לפני שמתבצעת בקשה להצגתם.
מטמון קודם
באמצעות האסטרטגיה הזו, קובץ השירות מחפש את הבקשה התואמת במטמון ומחזיר את התגובה המתאימה אם היא שמורה במטמון. אחרת, התגובה מאוחזרת מהרשת (אפשרות נוספת: עדכון המטמון לשיחות עתידיות). אם אין תגובה מהזיכרון או מהרשת, הבקשה תיכשל. הצגת נכסים בלי לפנות לרשת בדרך כלל מהירה יותר, ולכן האסטרטגיה הזו נותנת עדיפות לביצועים על פני עדכניות.
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. היא בודקת אם אפשר למלא את הבקשה מהרשת, ואם לא, מנסה לאחזר אותה מהמטמון. כמו במטמון קודם. אם אין תגובה מהרשת ואין תגובה מהזיכרון, הבקשה תיכשל. קבלת התגובה מהרשת בדרך כלל איטית יותר מקבלת התגובה מהמטמון, ולכן האסטרטגיה הזו מתעדפת תוכן מעודכן במקום ביצועים.
self.addEventListener("fetch", event => {
event.respondWith(
fetch(event.request)
.catch(error => {
return caches.match(event.request) ;
})
);
});
Stale while revalidate
האסטרטגיה stale while revalidate מחזירה תשובה שנשמרה במטמון באופן מיידי, ואז בודקת אם יש עדכון ברשת. אם נמצא עדכון, התשובה שנשמרה במטמון מוחלפת. האסטרטגיה הזו תמיד יוצרת בקשה לרשת, כי גם אם נמצא משאב במטמון, היא תנסה לעדכן את מה שנמצא במטמון עם מה שהתקבל מהרשת, כדי להשתמש בגרסה המעודכנת בבקשה הבאה. לכן, האסטרטגיה הזו מאפשרת לכם ליהנות מהיתרונות של אסטרטגיית השירות המהיר של המטמון, וגם לעדכן את המטמון ברקע.
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 באינטרנט בלבד.
מטמון בלבד
השימוש באסטרטגיית מטמון בלבד מבטיח שהבקשות אף פעם לא מגיעות לרשת. כל הבקשות הנכנסות מקבלות מענה עם פריט מטמון שאוכלס מראש. בדוגמת הקוד הבאה נעשה שימוש ב-handler של האירוע fetch
עם השיטה match
של אחסון המטמון כדי להגיב למטמון בלבד:
self.addEventListener("fetch", event => {
event.respondWith(caches.match(event.request));
});
שיטות מותאמות אישית
האסטרטגיות שלמעלה הן אסטרטגיות נפוצות של שמירת נתונים במטמון, אבל אתם אחראים ל-service worker ולדרך שבה הבקשות מטופלות. אם אף אחת מהאפשרויות האלה לא מתאימה לצרכים שלכם, תוכלו ליצור אפשרות משלכם.
לדוגמה, אפשר להשתמש באסטרטגיה של רשת ראשונה עם פסק זמן כדי לתת עדיפות לתוכן מעודכן, אבל רק אם התגובה מופיעה בתוך סף שהגדרתם. אפשר גם למזג תגובה ששמורה במטמון עם תגובה מהרשת, וליצור תגובה מורכבת מ-service worker.
עדכון נכסים
יכול להיות שיהיה לכם קשה לשמור על עדכניות של הנכסים שנשמרו במטמון של אפליקציית ה-PWA. אחת הדרכים לעשות זאת היא באמצעות אסטרטגיית 'הצגה של נתונים ישנים בזמן אימות מחדש', אבל יש גם דרכים אחרות. בפרק העדכון תלמדו על טכניקות שונות לעדכון התוכן והנכסים של האפליקציה.