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

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

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

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

أماكن توفّر المنصة

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

const cacheAvailable = 'caches' in self;

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

  • 40
  • 16
  • 41
  • 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);

وفي كل حالة من هذه الحالات، يحل إدخال جديد محل أي إدخال حالي مطابق. ويستخدم ذلك قواعد المطابقة نفسها الموضّحة في القسم الخاص 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.

شكرًا

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