הממשק Cache API: מדריך קצר

למד כיצד להשתמש ב-API למטמון כדי להפוך את נתוני האפליקציה לזמינים במצב אופליין.

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

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

איפה השירות זמין?

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

const cacheAvailable = 'caches' in self;

תמיכה בדפדפן

  • 40
  • 16
  • 41
  • 11.1

מקור

ניתן לגשת לממשק ה-API של מטמון מחלון, מ-iframe, מ-worker או מ-service worker.

מה אפשר לאחסן

במטמון נשמר רק זוגות של אובייקטים Request ו-Response, שמייצגים בקשות ותגובות מסוג HTTP, בהתאמה. עם זאת, הבקשות והתגובות יכולות להכיל כל סוג של נתונים שניתן להעביר באמצעות HTTP.

כמה יחידות אפשר לאחסן?

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

יצירה ופתיחה של קובץ שמור

כדי לפתוח מטמון, משתמשים בשיטה caches.open(name) ומעבירים את שם המטמון כפרמטר יחיד. אם המטמון בעל השם לא קיים, הוא נוצר. השיטה הזו מחזירה Promise שזוהה עם האובייקט Cache.

const cache = await caches.open('my-cache');
// do something with cache...

הוספה למטמון

יש שלוש דרכים להוסיף פריט למטמון – add, addAll ו-put. כל שלוש השיטות מחזירות Promise.

cache.add

קודם כל, יש cache.add(). היא משתמשת בפרמטר אחד, Request או כתובת URL (string). היא שולחת בקשה לרשת ושומרת את התגובה במטמון. אם השליפה נכשלת או שקוד הסטטוס של התגובה לא נמצא בטווח של 200, שום דבר לא יישמר וה-Promise ידחה. שימו לב שלא ניתן לשמור בקשות ממקורות שונים שלא במצב CORS, כי הן מחזירות status של 0. אפשר לאחסן בקשות כאלה רק ב-put.

// Retreive data.json from the server and store the response.
cache.add(new Request('/data.json'));

// Retreive data.json from the server and store the response.
cache.add('/data.json');

cache.addAll

עכשיו יש cache.addAll(). היא פועלת בדומה ל-add(), אבל לוקחת מערך של אובייקטים של Request או כתובות URL (string). הפעולה הזו דומה לקריאה ל-cache.add לכל בקשה בנפרד, אלא ש-Promise דוחה אם בקשה יחידה לא נשמרת במטמון.

const urls = ['/weather/today.json', '/weather/tomorrow.json'];
cache.addAll(urls);

בכל אחד מהמקרים האלו, רשומה חדשה תחליף כל רשומה קיימת תואמת. הפעולה הזו משתמשת באותם כללי התאמה שמתוארים בקטע בנושא retrieving.

cache.put

לבסוף, יש את cache.put(), שמאפשר לכם לשמור תגובה מהרשת, או ליצור ולאחסן Response משלכם. נדרשים שני פרמטרים. הערך הראשון יכול להיות אובייקט Request או כתובת URL (string). השני חייב להיות Response, מהרשת או שנוצר על ידי הקוד שלך.

// Retrieve data.json from the server and store the response.
cache.put('/data.json');

// Create a new entry for test.json and store the newly created response.
cache.put('/test.json', new Response('{"foo": "bar"}'));

// Retrieve data.json from the 3rd party site and store the response.
cache.put('https://example.com/data.json');

השיטה put() יותר מגבילה מאשר add() או addAll(), ותאפשר לכם לאחסן תשובות שאינן CORS, או תגובות אחרות שבהן קוד הסטטוס של התשובה הוא לא בטווח של 200. היא תחליף את כל התגובות הקודמות לאותה בקשה.

יצירת אובייקטים של בקשה

יוצרים את האובייקט Request באמצעות כתובת URL של הדבר שמאוחסן:

const request = new Request('/my-data-store/item-id');

עבודה עם אובייקטים של 'תגובה'

בונה האובייקטים Response מקבל סוגים רבים של נתונים, כולל Blob, ArrayBuffer, FormData אובייקטים ומחרוזות.

const imageBlob = new Blob([data], {type: 'image/jpeg'});
const imageResponse = new Response(imageBlob);
const stringResponse = new Response('Hello world');

אפשר להגדיר את סוג ה-MIME של Response על ידי הגדרת הכותרת המתאימה.

  const options = {
    headers: {
      'Content-Type': 'application/json'
    }
  }
  const jsonResponse = new Response('{}', options);

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

שיטה תיאור
arrayBuffer מחזירה ArrayBuffer שמכיל את הגוף, בסדרה של בייטים.
blob הפונקציה מחזירה Blob. אם השדה Response נוצר באמצעות Blob, אז Blob החדש הוא מאותו סוג. אחרת, נעשה שימוש ב-Content-Type של Response.
text פירוש הבייטים של הגוף כמחרוזת בקידוד UTF-8.
json פענוח הבייטים של הגוף כמחרוזת בקידוד UTF-8, ולאחר מכן ניסיון לניתוח הבייטים כ-JSON. מחזירה את האובייקט שמתקבל, או מציגה TypeError אם לא ניתן לנתח את המחרוזת כ-JSON.
formData מפרש את הבייטים של הגוף כטופס HTML, המקודד כ-multipart/form-data או application/x-www-form-urlencoded. מחזירה אובייקט FormData או מחזירה TypeError במקרה שלא ניתן לנתח את הנתונים.
body הפונקציה מחזירה ReadableStream לגוף הנתונים.

לדוגמה

const response = new Response('Hello world');
const buffer = await response.arrayBuffer();
console.log(new Uint8Array(buffer));
// Uint8Array(11) [72, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100]

אחזור ממטמון

כדי למצוא פריט במטמון, אפשר להשתמש בשיטה match.

const response = await cache.match(request);
console.log(request, response);

אם המחרוזת request היא מחרוזת, הדפדפן ממיר אותה ל-Request על ידי קריאה ל-new Request(request). הפונקציה מחזירה Promise שמבצעת שינוי אל Response אם נמצאה רשומה תואמת, או undefined אחרת.

כדי לקבוע אם שני מאפייני Requests תואמים, הדפדפן משתמש ביותר מכתובת ה-URL בלבד. שתי בקשות נחשבות שונות אם יש להן מחרוזות שאילתה שונות, כותרות Vary או שיטות HTTP שונות (GET, POST, PUT וכו').

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

const options = {
  ignoreSearch: true,
  ignoreMethod: true,
  ignoreVary: true
};

const response = await cache.match(request, options);
// do something with the response

אם יותר מבקשה אחת במטמון תואמת, הבקשה שנוצרה ראשונה מוחזרת. כדי לאחזר את כל התשובות התואמות, אפשר להשתמש ב-cache.matchAll().

const options = {
  ignoreSearch: true,
  ignoreMethod: true,
  ignoreVary: true
};

const responses = await cache.matchAll(request, options);
console.log(`There are ${responses.length} matching responses.`);

כקיצור דרך, אתם יכולים לחפש בכל המטמון בבת אחת באמצעות caches.match() במקום להפעיל cache.match() לכל מטמון.

מתבצע חיפוש

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

סינון

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

async function findImages() {
  // Get a list of all of the caches for this origin
  const cacheNames = await caches.keys();
  const result = [];

  for (const name of cacheNames) {
    // Open the cache
    const cache = await caches.open(name);

    // Get a list of entries. Each item is a Request object
    for (const request of await cache.keys()) {
      // If the request URL matches, add the response to the result
      if (request.url.endsWith('.png')) {
        result.push(await cache.match(request));
      }
    }
  }

  return result;
}

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

יצירת אינדקס

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

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

מחיקת פריט

כדי למחוק פריט ממטמון:

cache.delete(request);

כאשר הבקשה יכולה להיות Request או מחרוזת של כתובת URL. השיטה הזו גם לוקחת את אותם אובייקט אפשרויות כמו cache.match, וכך אפשר למחוק כמה צמדים של Request/Response לאותה כתובת URL.

cache.delete('/example/file.txt', {ignoreVary: true, ignoreSearch: true});

מחיקת מטמון

כדי למחוק מטמון, צריך לבצע קריאה ל-caches.delete(name). הפונקציה הזו מחזירה Promise שמפנה ל-true אם המטמון היה קיים ונמחק, או false אחרת.

תודה

תודה ל-Mat Scales שכתב את הגרסה המקורית של המאמר הזה, שהופיע לראשונה ב-WebFundamentals.