في بعض الحالات، قد يحتاج تطبيق الويب إلى إنشاء قناة اتصال ثنائية الاتجاه بين الصفحة ومشغّل الخدمات.
على سبيل المثال: في تطبيق PWA للبودكاست، يمكن إنشاء ميزة تتيح للمستخدم تنزيل الحلقات لمشاهدتها بلا إنترنت والسماح لعامل الخدمة بإبقاء الصفحة على اطّلاع بمستوى التقدّم بانتظام، لكي تتمكّن سلسلة التعليمات الرئيسية من تعديل واجهة المستخدم.
سنستكشف في هذا الدليل الطرق المختلفة لتنفيذ اتصال ثنائي بين سياق Window ومشغِّل الخدمات، من خلال استكشاف واجهات برمجة التطبيقات المختلفة ومكتبة Workspace، بالإضافة إلى بعض الحالات المتقدمة.
![رسم بياني يعرض عامل خدمات وصفحة يتبادلان الرسائل](https://web.dev/static/articles/two-way-communication-guide/image/diagram-showing-service-8bb189d1e9e40.png?authuser=19&hl=ar)
استخدام Workbox
workbox-window
هي مجموعة من وحدات مكتبة Workspace التي تهدف
إلى تشغيلها في سياق النافذة. توفر الفئة Workbox
طريقة messageSW()
لإرسال رسالة إلى عامل الخدمة المسجّل للمثيل وانتظار الردّ.
ينشئ رمز الصفحة التالي مثيل Workbox
جديدًا ويرسل رسالة إلى مشغّل الخدمات
للحصول على الإصدار الخاص به:
const wb = new Workbox('/sw.js');
wb.register();
const swVersion = await wb.messageSW({type: 'GET_VERSION'});
console.log('Service Worker version:', swVersion);
وينفِّذ مشغّل الخدمة أداة معالجة الرسائل على الجهة الأخرى، ويردّ على مشغّل الخدمة المسجَّل على النحو التالي:
const SW_VERSION = '1.0.0';
self.addEventListener('message', (event) => {
if (event.data.type === 'GET_VERSION') {
event.ports[0].postMessage(SW_VERSION);
}
});
تستخدم المكتبة واجهة برمجة تطبيقات خاصة بالمتصفّح سنراجعها في القسم التالي: قناة الرسائل، وهي تعرض تفاصيل عديدة حول طريقة التنفيذ، ما يسهِّل استخدامها، مع الاستفادة في الوقت نفسه من التوافق مع المتصفحات الواسعة التي توفّرها واجهة برمجة التطبيقات هذه.
![رسم بياني يعرض التواصل الثنائي بين الصفحة ومشغّل الخدمات باستخدام نافذة Workbox Window.](https://web.dev/static/articles/two-way-communication-guide/image/diagram-showing-way-comm-3573ad75f3cd1.png?authuser=19&hl=ar)
استخدام واجهات برمجة تطبيقات المتصفّح
إذا لم تكن مكتبة Workbox كافية لاحتياجاتك، ستتوفر العديد من واجهات برمجة التطبيقات ذات المستوى الأدنى لتنفيذ الاتصال "ثنائي الاتجاه" بين الصفحات ومشغّلي الخدمات. وهما بعض أوجه التشابه والاختلاف:
أوجه التشابه:
- في جميع الحالات، يبدأ الاتصال من طرف واحد من خلال واجهة
postMessage()
ويتم استقباله من الطرف الآخر من خلال تنفيذ معالجmessage
. - من الناحية العملية، تتيح لنا جميع واجهات برمجة التطبيقات المتاحة تنفيذ حالات الاستخدام نفسها، إلا أنّ بعضها قد يسهّل عملية التطوير في بعض السيناريوهات.
الاختلافات:
- لديهم طرق مختلفة لتحديد الجانب الآخر من التواصل: يستخدم بعضها إشارة صريحة إلى السياق الآخر، بينما يمكن للآخرين التواصل ضمنيًا عبر كائن وكيل تم إنشاؤه على كل جانب.
- ويختلف دعم المتصفح من نوع إلى آخر.
![رسم بياني يوضّح الاتصال الثنائي بين الصفحة ومشغّل الخدمات وواجهات برمجة التطبيقات المتوفّرة للمتصفّح](https://web.dev/static/articles/two-way-communication-guide/image/diagram-showing-way-comm-7b2a46c7cc95.png?authuser=19&hl=ar)
واجهة برمجة تطبيقات قناة البث
تتيح Broadcast Channel API إمكانية التواصل الأساسي بين سياقات التصفح عبر عناصر BroadcastChannel.
لتنفيذ هذه الميزة، يجب أولاً إنشاء مثيل لكائن BroadcastChannel
باستخدام المعرّف نفسه
وإرسال الرسائل وتلقّيها منه:
const broadcast = new BroadcastChannel('channel-123');
يعرض كائن BroadcastChannel واجهة postMessage()
لإرسال رسالة إلى أي سياق استماع:
//send message
broadcast.postMessage({ type: 'MSG_ID', });
يمكن لأي سياق متصفّح يمكن الاستماع إلى الرسائل باستخدام طريقة onmessage
لكائن BroadcastChannel
:
//listen to messages
broadcast.onmessage = (event) => {
if (event.data && event.data.type === 'MSG_ID') {
//process message...
}
};
وكما يتضح من الرسم البياني، ليست هناك إشارة واضحة إلى سياق معيّن، وبالتالي ليست هناك حاجة للحصول على مرجع أولاً لمشغِّل الخدمات أو لأي عميل معيّن.
![رسم بياني يعرض الاتصال الثنائي بين الصفحة ومشغّل الخدمات، باستخدام عنصر "قناة بث"](https://web.dev/static/articles/two-way-communication-guide/image/diagram-showing-way-comm-d328101990fe9.png?authuser=19&hl=ar)
ومن العيوب في هذه الحالة أنّه حتى وقت كتابة هذا النص، تصبح واجهة برمجة التطبيقات متوافقة مع Chrome وFirefox وEdge، ولكن هناك متصفّحات أخرى مثل Safari لا تتوافق معها بعد.
واجهة برمجة تطبيقات العميل
تسمح لك Client API بالحصول على
مرجع لجميع كائنات WindowClient
التي تمثل علامات التبويب النشطة التي يتحكّم فيها مشغّل الخدمات.
وبما أنّ عامل تشغيل الخدمات يتحكّم في الصفحة، فإنّها تستمع إلى الرسائل وترسلها إلى
مشغّل الخدمات النشط مباشرةً من خلال واجهة serviceWorker
:
//send message
navigator.serviceWorker.controller.postMessage({
type: 'MSG_ID',
});
//listen to messages
navigator.serviceWorker.onmessage = (event) => {
if (event.data && event.data.type === 'MSG_ID') {
//process response
}
};
وبالمثل، يستمع مشغّل الخدمات إلى الرسائل من خلال تطبيق أداة معالجة onmessage
:
//listen to messages
self.addEventListener('message', (event) => {
if (event.data && event.data.type === 'MSG_ID') {
//Process message
}
});
يحصل مشغّل الخدمات على مجموعة من عناصر WindowClient
من خلال تنفيذ طُرق مثل Clients.matchAll()
وClients.get()
للتواصل مرة أخرى مع أي من عملائه. ويمكن بعد ذلك
postMessage()
تنفيذ أي مما يلي:
//Obtain an array of Window client objects
self.clients.matchAll(options).then(function (clients) {
if (clients && clients.length) {
//Respond to last focused tab
clients[0].postMessage({type: 'MSG_ID'});
}
});
![رسم بياني يوضّح عامل خدمات يتواصل مع مجموعة من العملاء](https://web.dev/static/articles/two-way-communication-guide/image/diagram-showing-service-6eb1e0aa007c2.png?authuser=19&hl=ar)
يُعد Client API
خيارًا جيدًا للتواصل بسهولة مع جميع علامات التبويب النشطة من مشغّل الخدمات بطريقة مباشرة نسبيًا. تتوافق واجهة برمجة التطبيقات مع جميع المتصفحات
الرئيسية،
ولكن قد لا تتوفر كل الطرق المتوفرة لها، لذا احرص على التحقق من دعم المتصفح قبل
تنفيذها على موقعك الإلكتروني.
قناة الرسائل
تتطلب قناة الرسائل تحديد منفذ وتمريره من سياق إلى آخر لإنشاء قناة اتصال ثنائي الاتجاه.
لإعداد القناة، تنشئ الصفحة مثيلاً لعنصر MessageChannel
وتستخدمه
لإرسال منفذ إلى مشغّل الخدمات المسجَّل. وتنفّذ الصفحة أيضًا أداة معالجة بيانات onmessage
عليها
لتلقّي الرسائل من السياق الآخر:
const messageChannel = new MessageChannel();
//Init port
navigator.serviceWorker.controller.postMessage({type: 'PORT_INITIALIZATION'}, [
messageChannel.port2,
]);
//Listen to messages
messageChannel.port1.onmessage = (event) => {
// Process message
};
![رسم بياني يعرض صفحة تمرر منفذًا إلى مشغّل خدمات لإنشاء اتصال ثنائي الاتجاه.](https://web.dev/static/articles/two-way-communication-guide/image/diagram-showing-page-pas-57eb808e284e1.png?authuser=19&hl=ar)
يتلقى مشغّل الخدمة المنفذ ويحفظ مرجعًا له ويستخدمه لإرسال رسالة إلى الجانب الآخر:
let communicationPort;
//Save reference to port
self.addEventListener('message', (event) => {
if (event.data && event.data.type === 'PORT_INITIALIZATION') {
communicationPort = event.ports[0];
}
});
//Send messages
communicationPort.postMessage({type: 'MSG_ID'});
تتوافق "MessageChannel
" حاليًا مع جميع المتصفحات
الرئيسية.
واجهات برمجة التطبيقات المتقدمة: المزامنة في الخلفية والجلب في الخلفية
اكتشفنا في هذا الدليل طرقًا لتنفيذ أساليب اتصال ثنائي الاتجاه، في حالات بسيطة نسبيًا، مثل تمرير رسالة سلسلة تصف العملية المطلوب تنفيذها أو قائمة بعناوين URL المخزّنة مؤقتًا من سياق إلى آخر. سوف نستكشف في هذا القسم واجهتي برمجة تطبيقات للتعامل مع سيناريوهات معينة: نقص الاتصال والتنزيلات الطويلة.
المزامنة في الخلفية
قد يرغب تطبيق الدردشة في ضمان عدم فقدان الرسائل مطلقًا بسبب ضعف الاتصال. تتيح لك واجهة برمجة التطبيقات لمزامنة الخلفية تأجيل الإجراءات لتتم إعادة محاولة تنفيذ الإجراءات عندما يكون لدى المستخدم اتصال مستقر بالإنترنت. هذا مفيد لضمان أن كل ما يريد المستخدم إرساله، يتم إرساله بالفعل.
بدلاً من واجهة postMessage()
، تسجِّل الصفحة sync
:
navigator.serviceWorker.ready.then(function (swRegistration) {
return swRegistration.sync.register('myFirstSync');
});
يرصد مشغّل الخدمات بعد ذلك حدث sync
لمعالجة الرسالة:
self.addEventListener('sync', function (event) {
if (event.tag == 'myFirstSync') {
event.waitUntil(doSomeStuff());
}
});
من المفترض أن تعرض الدالة doSomeStuff()
وعدًا يشير إلى نجاح/فشل كل ما تحاول تنفيذه. وإذا تمت المحاولة، تكون المزامنة قد اكتملت. وإذا فشلت، فستتم جدولة مزامنة أخرى
لإعادة المحاولة. تعمل إعادة محاولة المزامنة أيضًا على انتظار الاتصال، واستخدام تراجع أسي.
بعد تنفيذ العملية، يمكن لعامل الخدمة التواصل مرة أخرى مع الصفحة لتحديث واجهة المستخدم، وذلك باستخدام أي من واجهات برمجة تطبيقات الاتصالات التي تم استكشافها سابقًا.
يستخدم بحث Google ميزة "المزامنة في الخلفية" للاحتفاظ بطلبات البحث التي تعذّر تنفيذها بسبب ضعف الاتصال، وإعادة المحاولة لاحقًا عندما يكون المستخدم متصلاً بالإنترنت. بمجرد تنفيذ العملية، يبلغون بالنتيجة إلى المستخدم عبر إشعار فوري على الويب:
![رسم بياني يعرض صفحة تمرر منفذًا إلى مشغّل خدمات لإنشاء اتصال ثنائي الاتجاه.](https://web.dev/static/articles/two-way-communication-guide/image/diagram-showing-page-pas-4ed0ed40b8bc1.png?authuser=19&hl=ar)
استرجاع البيانات في الخلفية
بالنسبة إلى أجزاء العمل القصيرة نسبيًا مثل إرسال رسالة أو قائمة بعناوين URL لتخزينها مؤقتًا، تعد الخيارات التي تم استكشافها حتى الآن اختيارًا جيدًا. وإذا استغرقت المهمة وقتًا طويلاً جدًا، فسيقتل المتصفح عامل الخدمة، وإلا فسيعد ذلك خطرًا على خصوصية المستخدم وطاقة البطارية.
تتيح لك Background Fetch API تقليل عبء المهام الطويلة إلى مشغّل الخدمات، مثل تنزيل الأفلام أو ملفات البودكاست أو مستويات اللعبة.
للتواصل مع مشغّل الخدمات من الصفحة، استخدِم backgroundFetch.fetch
بدلاً من postMessage()
:
navigator.serviceWorker.ready.then(async (swReg) => {
const bgFetch = await swReg.backgroundFetch.fetch(
'my-fetch',
['/ep-5.mp3', 'ep-5-artwork.jpg'],
{
title: 'Episode 5: Interesting things.',
icons: [
{
sizes: '300x300',
src: '/ep-5-icon.png',
type: 'image/png',
},
],
downloadTotal: 60 * 1024 * 1024,
},
);
});
يسمح الكائن BackgroundFetchRegistration
للصفحة بالاستماع إلى حدث progress
لمتابعة مستوى تقدّم عملية التنزيل:
bgFetch.addEventListener('progress', () => {
// If we didn't provide a total, we can't provide a %.
if (!bgFetch.downloadTotal) return;
const percent = Math.round(
(bgFetch.downloaded / bgFetch.downloadTotal) * 100,
);
console.log(`Download progress: ${percent}%`);
});
![رسم بياني يعرض صفحة تمرر منفذًا إلى مشغّل خدمات لإنشاء اتصال ثنائي الاتجاه.](https://web.dev/static/articles/two-way-communication-guide/image/diagram-showing-page-pas-e7b1a5ce84bde.png?authuser=19&hl=ar)
الخطوات التالية
استكشفنا في هذا الدليل الحالة الأكثر عمومية للتواصل بين الصفحة وعاملي الخدمة (اتصال ثنائي الاتجاه).
وفي كثير من الأحيان، قد يحتاج المرء إلى سياق واحد فقط للتواصل مع الآخر، دون تلقي رد. راجع الأدلّة التالية للحصول على إرشادات حول كيفية تنفيذ الأساليب الأحادية الاتجاه في صفحاتك من عامل الخدمة وإليه، بالإضافة إلى حالات الاستخدام وأمثلة الإنتاج:
- دليل التخزين المؤقت النهائي: دعوة عامل خدمات من الصفحة إلى تخزين الموارد في ذاكرة التخزين المؤقت مسبقًا (مثلاً في سيناريوهات الجلب المُسبَق).
- إشعارات البث: الاتصال بالصفحة من مشغّل الخدمات للإبلاغ عن تحديثات مهمة (على سبيل المثال، توفُّر إصدار جديد من تطبيق الويب)