واجهة برمجة تطبيقات ذاكرة التخزين المؤقت: دليل سريع

تعرَّف على كيفية استخدام واجهة برمجة التطبيقات Cache API لتوفير بيانات تطبيقك بلا اتصال بالإنترنت.

Cache API هو نظام لتخزين طلبات الشبكة واستردادها والردود المقابلة لها. قد تكون هذه طلبات قياسية وردود تم إنشاؤها أثناء تشغيل تطبيقك، أو قد يتم إنشاؤها فقط بغرض تخزين البيانات لاستخدامها لاحقًا.

تم إنشاء واجهة برمجة التطبيقات Cache API لتفعيل مهام الخدمة في تخزين طلبات الشبكة مؤقتًا كي تتمكّن من تقديم ردود سريعة، بغض النظر عن سرعة الشبكة أو مدى توفّرها. ومع ذلك، يمكن أيضًا استخدام واجهة برمجة التطبيقات كآلية تخزين عامة.

تتوفّر واجهة برمجة التطبيقات Cache API في جميع المتصفحات الحديثة. ويتم عرضها من خلال الموقع الشامل caches، ما يتيح لك اختبار توفّر واجهة برمجة التطبيقات باستخدام ميزة رصد بسيطة:

const cacheAvailable = 'caches' in self;

توافق المتصفّح

  • Chrome: 40
  • الحافة: 16.
  • Firefox: 41.
  • ‫Safari: 11.1

المصدر

يمكن الوصول إلى واجهة برمجة التطبيقات Cache API من نافذة أو إطار iframe أو عامل أو مشغّل خدمات.

المحتوى الذي يمكن تخزينه

لا تخزِّن ذاكرات التخزين المؤقت سوى أزواج من كائنَي 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);

في كلّ من هذه الحالات، يستبدل إدخال جديد أيّ إدخال حالي مطابق. ويستخدم هذا الإجراء قواعد المطابقة نفسها الموضّحة في قسم الاسترداد.

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

يقبل عنصر الإنشاء 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 لfiltrare الإدخالات. يُرجى العِلم أنّ هذا الإجراء بطيء إذا كنت تبحث في مجموعات كبيرة من البيانات.

إنشاء فهرس

الطريقة الأخرى لتنفيذ عملية البحث هي الاحتفاظ بفهرس منفصل لملف السجلّ الذي يمكن البحث فيه وتخزين الفهرس في 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 في الحالات الأخرى.

شكرًا

نشكر "مات سكيلز" الذي كتب النسخة الأصلية من هذه المقالة، والتي ظهرت لأول مرة على WebFundamentals.