حتى الآن، لم يتم إدراج سوى إشارات ومقتطفات رموز صغيرة من واجهة Cache
.
لاستخدام عاملي الخدمة بشكل فعّال، يجب اعتماد استراتيجية تخزين مؤقت واحدة أو أكثر، ما يتطلّب بعض المعرفة بواجهة Cache
.
استراتيجية التخزين المؤقت هي تفاعل بين حدث fetch
لدى مشغّل الخدمات وواجهة Cache
.
وتعتمد كيفية كتابة استراتيجية التخزين المؤقت على ذلك،
على سبيل المثال، قد يُفضَّل التعامل مع طلبات الأصول الثابتة بشكلٍ مختلف عن المستندات،
ويؤثر ذلك في طريقة إنشاء استراتيجية التخزين المؤقت.
قبل أن ندخل في الإستراتيجيات بذاتها،
لنتوقف قليلاً للحديث عن ماهية واجهة Cache
، وماهية تلك الواجهة،
وملخص سريع عن بعض الأساليب التي توفرها لإدارة ذاكرة التخزين المؤقت لعامل الخدمة.
واجهة Cache
مقابل ذاكرة التخزين المؤقت لـ HTTP
إذا لم يسبق لك العمل على واجهة Cache
،
قد يكون من المغري استخدام واجهة برمجة التطبيقات المماثلة لذاكرة التخزين المؤقت لبروتوكول HTTP أو على الأقل مرتبطة بها. لكن الأمر ليس كذلك.
- وواجهة
Cache
هي آلية تخزين مؤقت منفصلة تمامًا عن ذاكرة التخزين المؤقت لبروتوكول HTTP. - أيًا كانت إعدادات
Cache-Control
التي تستخدمها للتأثير في ذاكرة التخزين المؤقت لبروتوكول HTTP، ليس لها أي تأثير على مواد العرض التي يتم تخزينها في واجهةCache
.
من المفيد النظر إلى ذاكرات التخزين المؤقت للمتصفح على أنها طبقات. ذاكرة التخزين المؤقت لـ HTTP هي ذاكرة تخزين مؤقت منخفضة المستوى تستند إلى أزواج المفاتيح/القيم مع توجيهات يتم التعبير عنها في عناوين HTTP.
في المقابل، إنّ واجهة Cache
هي ذاكرة تخزين مؤقت عالية المستوى يتم تشغيلها باستخدام JavaScript API.
يوفّر هذا الأمر مرونة أكبر مقارنةً باستخدام أزواج مفاتيح/قيم HTTP مبسّطة نسبيًا، ويشكل أحد نصف ما يجعل استراتيجيات التخزين المؤقت ممكنة.
في ما يلي بعض طرق واجهة برمجة التطبيقات المهمة بشأن ذاكرات التخزين المؤقت لمشغِّلات الخدمات:
CacheStorage.open
لإنشاء مثيلCache
جديد.Cache.add
وCache.put
لتخزين استجابات الشبكة في ذاكرة التخزين المؤقت لمشغِّل الخدمات.Cache.match
لتحديد موقع استجابة مخبأة في مثيلCache
.Cache.delete
لإزالة استجابة مخزّنة مؤقتًا من مثيلCache
.
هذه بعض الأمثلة فقط. هناك طرق أخرى مفيدة، ولكن هذه هي الطرق الأساسية التي ستلاحظ استخدامها لاحقًا في هذا الدليل.
حدث fetch
المتواضع
أما النصف الآخر من استراتيجية التخزين المؤقت، فهو حدث fetch
لعامل الخدمة.
لقد سمعت حتى الآن من خلال هذه المستندات بعض المعلومات عن "اعتراض طلبات الشبكة"،
وحدث fetch
داخل عامل الخدمة في ما يلي:
// Establish a cache name
const cacheName = 'MyFancyCacheName_v1';
self.addEventListener('install', (event) => {
event.waitUntil(caches.open(cacheName));
});
self.addEventListener('fetch', async (event) => {
// Is this a request for an image?
if (event.request.destination === 'image') {
// Open the cache
event.respondWith(caches.open(cacheName).then((cache) => {
// Respond with the image from the cache or from the network
return cache.match(event.request).then((cachedResponse) => {
return cachedResponse || fetch(event.request.url).then((fetchedResponse) => {
// Add the network response to the cache for future visits.
// Note: we need to make a copy of the response to save it in
// the cache and use the original as the request response.
cache.put(event.request, fetchedResponse.clone());
// Return the network response
return fetchedResponse;
});
});
}));
} else {
return;
}
});
هذا مثال على لعبة، ومثال يمكنك مشاهدته عمليًا بنفسك، لكنه يقدّم لمحة عما يمكن للعاملين تنفيذه. يُنفّذ الرمز البرمجي أعلاه ما يلي:
- افحص سمة
destination
للطلب لمعرفة ما إذا كان هذا طلب صورة. - إذا كانت الصورة في ذاكرة التخزين المؤقت لمشغِّل الخدمات، يمكنك عرضها من هناك. وإذا لم يكن الأمر كذلك، أحضر الصورة من الشبكة واخزّن الاستجابة في ذاكرة التخزين المؤقت وأرجع استجابة الشبكة.
- ويتم تمرير جميع الطلبات الأخرى من خلال مشغّل الخدمات بدون تفاعل مع ذاكرة التخزين المؤقت.
يحتوي عنصر event
في الجلب على
سمة request
التي تتضمّن بعض المعلومات المفيدة لمساعدتك في تحديد نوع كل طلب:
url
، وهو عنوان URL لطلب الشبكة الذي يعالجه حاليًا حدثfetch
.method
، وهي طريقة الطلب (على سبيل المثال،GET
أوPOST
).mode
، التي تصف وضع الطلب. غالبًا ما يتم استخدام القيمة'navigate'
للتمييز بين طلبات مستندات HTML والطلبات الأخرى.destination
، الذي يصف نوع المحتوى المطلوب بطريقة تتجنّب استخدام امتداد ملف مادة العرض المطلوبة.
مصطلح "غير متزامن" هو اسم اللعبة.
تذكَّر أنّ فعالية install
تقدّم طريقة
event.waitUntil
التي تستغرق وقتًا وعدًا، وتنتظر حتى يتم حلّ المشكلة قبل مواصلة التفعيل.
يقدّم حدث fetch
طريقة event.respondWith
مماثلة يمكنك استخدامها لعرض نتيجة
طلب fetch
غير متزامن
أو ردّ تم عرضه باستخدام طريقة match
لواجهة Cache
.
استراتيجيات التخزين المؤقت
الآن بعد أن أصبحت على دراية بسيطة باستخدام مثيلات Cache
ومعالج أحداث fetch
،
أصبحت جاهزًا للتعمق في بعض استراتيجيات التخزين المؤقت للعاملين في مجال الخدمات.
على الرغم من أن الاحتمالات لا حصر لها من الناحية العملية، إلا أن هذا الدليل
سيلتزم بالاستراتيجيات التي يتم تطبيقها مع Workbox،
حتى تتمكن من التعرف على ما يحدث في العناصر الداخلية لـ Workbox.
ذاكرة التخزين المؤقت فقط
لنبدأ باستراتيجية تخزين مؤقت بسيطة سنطلق عليها اسم "ذاكرة التخزين المؤقت فقط". الهدف من ذلك هو أنه عندما يكون مشغّل الخدمات هو المتحكّم في الصفحة، ستذهب الطلبات المطابقة إلى ذاكرة التخزين المؤقت فقط. يعني ذلك أنّه يجب تخزين أي مواد عرض مخزّنة مؤقتًا بشكل مسبق لتصبح متاحة للنمط، وأنّ مواد العرض هذه لن يتمّ تحديثها أبدًا في ذاكرة التخزين المؤقت إلى أن يتم تحديث مشغّل الخدمة.
// Establish a cache name
const cacheName = 'MyFancyCacheName_v1';
// Assets to precache
const precachedAssets = [
'/possum1.jpg',
'/possum2.jpg',
'/possum3.jpg',
'/possum4.jpg'
];
self.addEventListener('install', (event) => {
// Precache assets on install
event.waitUntil(caches.open(cacheName).then((cache) => {
return cache.addAll(precachedAssets);
}));
});
self.addEventListener('fetch', (event) => {
// Is this one of our precached assets?
const url = new URL(event.request.url);
const isPrecachedRequest = precachedAssets.includes(url.pathname);
if (isPrecachedRequest) {
// Grab the precached asset from the cache
event.respondWith(caches.open(cacheName).then((cache) => {
return cache.match(event.request.url);
}));
} else {
// Go to the network
return;
}
});
أعلاه، يتم تخزين مصفوفة من مواد العرض مؤقتًا عند التثبيت.
عند معالجة عامل الخدمة لعمليات الجلب،
نتحقق مما إذا كان عنوان URL للطلب الذي يعالجه الحدث fetch
ضمن مصفوفة مواد العرض المخزنة مؤقتًا بشكل مسبق.
إذا كان الأمر كذلك، فسنحصل على المورد من ذاكرة التخزين المؤقت ونتخطى الشبكة.
تمر الطلبات الأخرى عبر الشبكة،
والشبكة فقط.
للاطّلاع على هذه الاستراتيجية عمليًا،
يمكنك الاطّلاع على هذا العرض التوضيحي بعد فتح وحدة التحكّم.
الشبكة فقط
على عكس "ذاكرة التخزين المؤقت فقط" هي "الشبكة فقط"، حيث يتم تمرير طلب من خلال مشغّل الخدمات إلى الشبكة بدون أي تفاعل مع ذاكرة التخزين المؤقت لعامل الخدمة. وهذه استراتيجية جيدة لضمان حداثة المحتوى (فكّر في الترميز)، ولكن المفاضلة هي أنها لن تعمل أبدًا عندما يكون المستخدم غير متصل بالإنترنت.
إنّ ضمان تمرير الطلب إلى الشبكة يعني عدم الاتصال بـ event.respondWith
لتقديم طلب مطابق.
إذا كنت تريد أن يكون الفيديو صريحًا،
يمكنك وضع علامة return;
فارغة في استدعاء حدث fetch
للطلبات التي تريد تمريرها إلى الشبكة.
هذا ما يحدث في العرض التوضيحي لاستراتيجية "ذاكرة التخزين المؤقت فقط" للطلبات التي لا يتم تخزينها مؤقتًا.
التخزين المؤقت أولاً، للرجوع إلى الشبكة
هذه الاستراتيجية هي المكان الذي تصبح فيه الأمور أكثر انخراطًا. بالنسبة للطلبات المطابقة، تسير العملية على النحو التالي:
- يصل الطلب إلى ذاكرة التخزين المؤقت. إذا كانت مادة العرض متوفّرة في ذاكرة التخزين المؤقت، يمكنك عرضها من هناك.
- إذا لم يكن الطلب في ذاكرة التخزين المؤقت، انتقِل إلى الشبكة.
- بعد انتهاء طلب الشبكة، أضفه إلى ذاكرة التخزين المؤقت، ثم اعرض الاستجابة من الشبكة.
إليك مثال على هذه الاستراتيجية، ويمكنك اختبارها في عرض توضيحي مباشر:
// Establish a cache name
const cacheName = 'MyFancyCacheName_v1';
self.addEventListener('fetch', (event) => {
// Check if this is a request for an image
if (event.request.destination === 'image') {
event.respondWith(caches.open(cacheName).then((cache) => {
// Go to the cache first
return cache.match(event.request.url).then((cachedResponse) => {
// Return a cached response if we have one
if (cachedResponse) {
return cachedResponse;
}
// Otherwise, hit the network
return fetch(event.request).then((fetchedResponse) => {
// Add the network response to the cache for later visits
cache.put(event.request, fetchedResponse.clone());
// Return the network response
return fetchedResponse;
});
});
}));
} else {
return;
}
});
على الرغم من أن هذا المثال يتناول الصور فقط، فهذه استراتيجية رائعة لتطبيقها على جميع مواد العرض الثابتة (مثل CSS وJavaScript والصور والخطوط)، وخاصةً تلك ذات إصدار علامة التجزئة. توفّر هذه الميزة طريقة لزيادة سرعة مواد العرض غير القابلة للتغيير، وذلك من خلال تنفيذ أي عمليات تحقّق من حداثة المحتوى بشكلٍ جانبي على الخادم قد تبدأ ذاكرة التخزين المؤقت لبروتوكول HTTP. والأهم من ذلك، ستكون أي مواد عرض مخزّنة مؤقتًا متاحة بلا اتصال بالإنترنت.
الشبكة أولاً، ثم الرجوع إلى ذاكرة التخزين المؤقت
إذا كنت تريد قلب خيار "ذاكرة التخزين المؤقت أولًا ثم الشبكة ثانيًا" رأسًا على الأقدام، فسينتهي بك الأمر باستخدام استراتيجية "الشبكة أولاً، ثم ذاكرة التخزين المؤقت ثانيًا"، كما يبدو الأمر:
- تنتقل إلى الشبكة أولاً لطلب ما، وتضع الاستجابة في ذاكرة التخزين المؤقت.
- إذا كنت غير متصل بالإنترنت في وقت لاحق، فستعود إلى أحدث إصدار من هذه الاستجابة في ذاكرة التخزين المؤقت.
هذه الإستراتيجية رائعة لطلبات HTML أو واجهة برمجة التطبيقات عندما تريد، رغم أنك متصل بالإنترنت، تريد أحدث إصدار من المورد، إلا أنك تريد منح إمكانية الوصول في وضع عدم الاتصال لأحدث إصدار متاح. وفي ما يلي الشكل الذي قد يبدو عليه الأمر عند تطبيقه على طلبات HTML:
// Establish a cache name
const cacheName = 'MyFancyCacheName_v1';
self.addEventListener('fetch', (event) => {
// Check if this is a navigation request
if (event.request.mode === 'navigate') {
// Open the cache
event.respondWith(caches.open(cacheName).then((cache) => {
// Go to the network first
return fetch(event.request.url).then((fetchedResponse) => {
cache.put(event.request, fetchedResponse.clone());
return fetchedResponse;
}).catch(() => {
// If the network is unavailable, get
return cache.match(event.request.url);
});
}));
} else {
return;
}
});
يمكنك تجربة ذلك في عرض توضيحي. أولاً، انتقل إلى الصفحة. قد تحتاج إلى إعادة التحميل قبل وضع استجابة HTML في ذاكرة التخزين المؤقت. ثم في أدوات المطور، يمكنك محاكاة الاتصال بلا اتصال، وإعادة التحميل مرة أخرى. وسيتم عرض آخر نسخة متاحة على الفور من ذاكرة التخزين المؤقت.
في الحالات التي تكون فيها إمكانية الاتصال بلا إنترنت مهمة، ولكن تحتاج إلى تحقيق التوازن بين هذه الإمكانية وإمكانية الوصول إلى أحدث إصدار من بيانات الترميز أو واجهة برمجة التطبيقات، تُعدّ "الشبكة أولاً، ذاكرة التخزين المؤقت ثانيًا" استراتيجية قوية تحقق هذا الهدف.
إعادة التحقق القديمة
من بين الإستراتيجيات التي تناولناها حتى الآن، تعد "الاستراتيجيات القديمة إلى حين إعادة التحقق" هي الأكثر تعقيدًا. إنها تشبه آخر استراتيجيتين في بعض النواحي، ولكن الإجراء يعطي الأولوية لسرعة الوصول للمورد، مع الحفاظ على تحديثه أيضًا في الخلفية. تشبه هذه الاستراتيجية ما يلي:
- عند الطلب الأول لمادة عرض، أحضرها من الشبكة، ضعها في ذاكرة التخزين المؤقت، وأعد استجابة الشبكة.
- بناءً على الطلبات اللاحقة، يمكنك عرض مادة العرض من ذاكرة التخزين المؤقت أولاً، ثم "في الخلفية" وإعادة طلبها من الشبكة وتعديل إدخال ذاكرة التخزين المؤقت لمادة العرض.
- بالنسبة إلى الطلبات بعد ذلك، ستتلقى آخر نسخة تم جلبها من الشبكة والتي تم وضعها في ذاكرة التخزين المؤقت في الخطوة السابقة.
هذه استراتيجية ممتازة للأشياء التي تكون من المهم تحديثها باستمرار، ولكنها ليست ضرورية. فكر في أشياء مثل الصور الرمزية لموقع على وسائل التواصل الاجتماعي. يتم تحديثها عندما يحاول المستخدمون القيام بذلك، ولكن أحدث إصدار ليس ضروريًا تمامًا في كل طلب.
// Establish a cache name
const cacheName = 'MyFancyCacheName_v1';
self.addEventListener('fetch', (event) => {
if (event.request.destination === 'image') {
event.respondWith(caches.open(cacheName).then((cache) => {
return cache.match(event.request).then((cachedResponse) => {
const fetchedResponse = fetch(event.request).then((networkResponse) => {
cache.put(event.request, networkResponse.clone());
return networkResponse;
});
return cachedResponse || fetchedResponse;
});
}));
} else {
return;
}
});
يمكنك أن ترى ذلك عمليًّا في عرض توضيحي آخر مباشر، لا سيّما إذا كنت تنتبه إلى علامة تبويب "الشبكة" في أدوات المطوّرين على المتصفّح،
وعارض CacheStorage
الخاص به (إذا كانت أدوات المطوّرين في المتصفّح تتضمّن مثل هذه الأداة).
الانتقال إلى Workbox
يلخّص هذا المستند مراجعتنا لواجهة برمجة تطبيقات مشغّل الخدمات، بالإضافة إلى واجهات برمجة التطبيقات ذات الصلة، ما يعني أنك تعلمت ما يكفي حول كيفية استخدام مشغّلي الخدمات مباشرةً لبدء إصلاح مشاكل Workbox.