قد تحتاج بعض المواقع الإلكترونية إلى التواصل مع مشغّل الخدمة بدون الحاجة إلى إبلاغه بالنتيجة. في ما يلي بعض الأمثلة:
- ترسل الصفحة إلى مشغّل الخدمة قائمة بعناوين URL للجلب المسبق، بحيث عندما ينقر المستخدم على رابط، تكون الموارد الفرعية للمستند أو الصفحة متوفرة في ذاكرة التخزين المؤقت، ما يجعل التنقّل اللاحقة أسرع بكثير.
- تطلب الصفحة من مشغّل الخدمة استرداد مجموعة من أهم المقالات وتخزينها مؤقتًا لتوفيرها للاستخدام بلا اتصال بالإنترنت.
يساعد تفويض هذه الأنواع من المهام غير المُلحّة إلى عامل الخدمة في إخلاء سلسلة التعليمات الرئيسية من أجل معالجة المهام الأكثر إلحاحًا مثل الاستجابة لتفاعلات المستخدم.
![مخطّط بياني لصفحة تطلب تخزين الموارد في ذاكرة التخزين المؤقت لعامل خدمة.](https://web.dev/static/articles/imperative-caching-guide/image/diagram-a-page-requestin-f19107d4e4aec.png?authuser=6&hl=ar)
سنتعرّف في هذا الدليل على طريقة تنفيذ تقنية اتصال أحادي الاتجاه من الصفحة إلى مشغّل الخدمة باستخدام واجهات برمجة التطبيقات العادية للمتصفّح ومكتبة Workbox. نسمي هذه الأنواع من حالات الاستخدام التخزين المؤقت الضروري.
حالة الإنتاج
يطبّق الموقع الإلكتروني 1-800-Flowers.com التخزين المؤقت (الجلب المُسبَق) مع مشغِّلي الخدمات من خلال
postMessage()
لجلب أهم العناصر مسبقًا في صفحات الفئات لتسريع عملية الانتقال اللاحقة إلى صفحات تفاصيل المنتج.
![شعار من 1 إلى 800 زهرة.](https://web.dev/static/articles/imperative-caching-guide/image/logo-1-800-flowers-94f008b879df2.png?authuser=6&hl=ar)
ويستخدمون أسلوبًا مختلطًا لتحديد العناصر التي يجب جلبها مسبقًا:
- في وقت تحميل الصفحة، يطلب الفريق من عامل الخدمة استرداد بيانات JSON لأهم 9 عناصر، وإضافة كائنات الاستجابة الناتجة إلى ذاكرة التخزين المؤقت.
- وبالنسبة إلى العناصر المتبقية، يستمع المستخدم إلى الحدث
mouseover
، وبالتالي عندما يحرك المستخدم المؤشر أعلى العنصر، يمكنه تشغيل عملية جلب المورد عند "الطلب".
تستخدم ذاكرة التخزين المؤقت API لتخزين ردود JSON:
![شعار من 1 إلى 800 زهرة.](https://web.dev/static/articles/imperative-caching-guide/image/logo-1-800-flowers-ee1b000c680c6.png?authuser=6&hl=ar)
عندما ينقر المستخدم على عنصر ما، يمكن الحصول على بيانات JSON المرتبطة به من ذاكرة التخزين المؤقت، دون الحاجة إلى الانتقال إلى الشبكة، مما يجعل التنقل أسرع.
استخدام Workbox
يوفّر Workbox طريقة سهلة لإرسال الرسائل إلى مشغّل الخدمات، من خلال حزمة workbox-window
، وهي مجموعة من الوحدات المخصّصة للتشغيل في سياق النافذة. إنها مكمّلة لحزم Workbox الأخرى
التي يتم تشغيلها في عامل الخدمة.
لإبلاغ الصفحة بمشغِّل الخدمات، احصل أولاً على مرجع عنصر Workbox إلى مشغّل الخدمات المسجّل:
const wb = new Workbox('/sw.js');
wb.register();
ومن ثم يمكنك إرسال الرسالة مباشرةً بشكل بياني بدون مواجهة متاعب الحصول على التسجيل أو التحقق من التفعيل أو التفكير في واجهة برمجة تطبيقات الاتصال الأساسية:
wb.messageSW({"type": "PREFETCH", "payload": {"urls": ["/data1.json", "data2.json"]}}); });
ينفِّذ مشغّل الخدمة معالج message
للاستماع إلى هذه الرسائل. يمكن أن تعرض ردًّا اختياريًا، ولكنّه ليس ضروريًا في مثل هذه الحالات:
self.addEventListener('message', (event) => {
if (event.data && event.data.type === 'PREFETCH') {
// do something
}
});
استخدام واجهات برمجة التطبيقات للمتصفّح
إذا لم تكُن مكتبة Workbox كافية لاحتياجاتك، إليك كيفية تنفيذ نافذة للتواصل مع مشغّلي الخدمات باستخدام واجهات برمجة تطبيقات المتصفّح.
يمكن استخدام postMessage API لإنشاء آلية تواصل أحادية الاتجاه من الصفحة إلى مشغّل الخدمة.
تستدعي الصفحة postMessage()
في واجهة مشغّل الخدمات:
navigator.serviceWorker.controller.postMessage({
type: 'MSG_ID',
payload: 'some data to perform the task',
});
ينفِّذ مشغّل الخدمة معالج message
للاستماع إلى هذه الرسائل.
self.addEventListener('message', (event) => {
if (event.data && event.data.type === MSG_ID) {
// do something
}
});
السمة {type : 'MSG_ID'}
ليست مطلوبة على الإطلاق، لكنّها طريقة تتيح للصفحة إرسال أنواع مختلفة من التعليمات إلى عامل الخدمة (أي "الجلب المُسبَق" بدلاً من "محو مساحة التخزين"). يمكن أن يتفرع مشغّل الخدمة إلى مسارات تنفيذ مختلفة بناءً على هذه العلامة.
إذا نجحت العملية، فسيتمكن المستخدم من الحصول على الفوائد منها ولكن إذا لم يكن الأمر كذلك، فلن يغير تدفق المستخدم الرئيسي. على سبيل المثال، عندما يحاول 1-800-Flowers.com التخزين مؤقتًا بشكل مسبق، لن تحتاج الصفحة إلى معرفة ما إذا كان عامل الخدمة قد نجح أم لا. إذا كان الأمر كذلك، فسيستمتع المستخدم بتنقل أسرع. وإذا لم تكن الصفحة لا تزال بحاجة إلى الانتقال إلى الصفحة الجديدة. سيستغرق الأمر وقتًا أطول قليلاً.
مثال بسيط للجلب المسبق
يعد الجلب المسبق أحد أكثر تطبيقات التخزين المؤقت الضروري شيوعًا، وهو ما يعني جلب الموارد لعنوان URL معين قبل أن ينتقل المستخدم إليه لتسريع عملية التنقل.
هناك طرق مختلفة لتنفيذ الجلب المُسبَق في المواقع الإلكترونية:
- استخدام علامات الجلب المُسبَق للروابط في الصفحات: يتم الاحتفاظ بالموارد في ذاكرة التخزين المؤقت للمتصفّح لمدة خمس دقائق، وبعدها تسري قواعد
Cache-Control
العادية للموارد. - واستكمال التقنية السابقة باستخدام استراتيجية تخزين مؤقت وقت التشغيل لدى مشغّل الخدمات لإطالة عمر مورد الجلب المُسبَق بعد هذا الحدّ الأقصى.
بالنسبة إلى سيناريوهات الجلب المسبق البسيطة نسبيًا، مثل الجلب المسبق للمستندات أو مواد العرض المحددة (JS وCSS وغيرها)، فإن هذه الأساليب هي أفضل نهج.
وإذا تطلّب الأمر منطقًا إضافيًا، على سبيل المثال، تحليل مورد الجلب المُسبَق (ملف JSON أو صفحة) لاسترجاع عناوين URL الداخلية، من الأفضل تفويض هذه المهمة بالكامل إلى عامل الخدمة.
يمتلك تفويض هذه الأنواع من العمليات لعامل الخدمات المزايا التالية:
- نقل الأعباء الثقيلة للجلب ومعالجة ما بعد الجلب (والتي سيتم تقديمها لاحقًا) إلى سلسلة محادثات ثانوية من خلال القيام بذلك، يتم تحرير سلسلة التعليمات الرئيسية للتعامل مع مهام أكثر أهمية مثل الاستجابة لتفاعلات المستخدم.
- السماح لعدة عملاء (مثل علامات التبويب) بإعادة استخدام وظيفة مشتركة وحتى طلب الخدمة في الوقت نفسه بدون حظر سلسلة التعليمات الرئيسية
الجلب المُسبق لصفحات تفاصيل المنتجات
استخدِم أولاً postMessage()
على واجهة مشغّل الخدمات ومرِّر مجموعة من عناوين URL لتخزينها في ذاكرة التخزين المؤقت:
navigator.serviceWorker.controller.postMessage({
type: 'PREFETCH',
payload: {
urls: [
'www.exmaple.com/apis/data_1.json',
'www.exmaple.com/apis/data_2.json',
],
},
});
في مشغّل الخدمات، نفِّذ معالج message
لاعتراض ومعالجة الرسائل التي ترسلها أي علامة تبويب نشطة:
addEventListener('message', (event) => {
let data = event.data;
if (data && data.type === 'PREFETCH') {
let urls = data.payload.urls;
for (let i in urls) {
fetchAsync(urls[i]);
}
}
});
في الرمز البرمجي السابق، قدّمنا وظيفة مساعد صغيرة تُعرف باسم fetchAsync()
لتكرار مصفوفة عناوين URL وإصدار طلب استرجاع لكل عنوان منها:
async function fetchAsync(url) {
// await response of fetch call
let prefetched = await fetch(url);
// (optionally) cache resources in the service worker storage
}
عند الحصول على الاستجابة، يمكنك الاعتماد على رؤوس التخزين المؤقت للمورد. وعلى الرغم من ذلك، وكما هو الحال في صفحات تفاصيل المنتج، لا تُخزَّن الموارد في ذاكرة التخزين المؤقت (أي أنّها تحتوي على عنوان
Cache-control
بالقيمة no-cache
). وفي مثل هذه الحالات، يمكنك إلغاء هذا السلوك من خلال
تخزين المورد الذي تم جلبه في ذاكرة التخزين المؤقت لمشغّل الخدمات. وله فائدة إضافية تتمثل في السماح
بعرض الملف في سيناريوهات عدم الاتصال بالإنترنت.
ما بعد بيانات JSON
بعد استرجاع بيانات JSON من نقطة نهاية الخادم، غالبًا ما تحتوي تلك البيانات على عناوين URL أخرى تستحق الجلب المُسبَق، مثل صورة أو بيانات نقطة نهاية أخرى مرتبطة ببيانات المستوى الأول هذه.
لنفترض أن بيانات JSON المعروضة في المثال هي معلومات موقع تسوق البقالة:
{
"productName": "banana",
"productPic": "https://cdn.example.com/product_images/banana.jpeg",
"unitPrice": "1.99"
}
عدِّل رمز fetchAsync()
للتكرار التحسيني لقائمة المنتجات وتخزين الصورة الرئيسية لكل منتج منها مؤقتًا:
async function fetchAsync(url, postProcess) {
// await response of fetch call
let prefetched = await fetch(url);
//(optionally) cache resource in the service worker cache
// carry out the post fetch process if supplied
if (postProcess) {
await postProcess(prefetched);
}
}
async function postProcess(prefetched) {
let productJson = await prefetched.json();
if (productJson && productJson.product_pic) {
fetchAsync(productJson.product_pic);
}
}
يمكنك إضافة بعض الاستثناءات المتعلقة بهذا الرمز في حالات مثل أخطاء 404. غير أنّ جمال استخدام مشغّل الخدمات للجلب المُسبَق قد يفشل بدون أن يؤثر ذلك سلبًا على الصفحة وسلسلة المحادثات الرئيسية. قد يكون لديك أيضًا منطق أكثر تفصيلاً في مرحلة ما بعد المعالجة للمحتوى الذي يتم جلبه مسبقًا، ما يجعله أكثر مرونة وفصله عن البيانات التي يعالجها. لا حدود تواجهك سوى السماء.
الخلاصة
تناولنا في هذه المقالة حالة استخدام شائعة لإجراء اتصال أحادي الاتجاه بين الصفحة ومشغّل الخدمات، وهي: التخزين المؤقت الضروري. الأمثلة التي تمت مناقشتها مخصصة فقط لتوضيح طريقة واحدة لاستخدام هذا النمط ويمكن تطبيق النهج نفسه على حالات استخدام أخرى أيضًا، مثل التخزين المؤقت لأهم المقالات عند الطلب للمشاهدة بلا اتصال بالإنترنت ووضع الإشارات المرجعية وغيرها.
للاطّلاع على مزيد من أنماط التواصل مع الصفحات ومشغّلي الخدمات، يُرجى الاطّلاع على ما يلي:
- إشعارات البث: الاتصال بالصفحة من مشغّل الخدمات للإبلاغ عن تحديثات مهمة (على سبيل المثال، توفُّر إصدار جديد من تطبيق الويب)
- التواصل المتبادل: تفويض مهمة لعامل الخدمات (على سبيل المثال، عملية تنزيل ثقيلة)، وإبقاء الصفحة على اطّلاع بمستوى التقدم.