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

تعرَّف على كيفية استخدام واجهة برمجة التطبيقات 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 (strings). يعمل هذا الإجراء بالطريقة نفسها التي يعمل بها استدعاء 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.