التواصل الثنائي مع عاملي الخدمة

في بعض الحالات، قد يحتاج تطبيق الويب إلى إنشاء قناة تواصل ثنائية الاتجاه بين الصفحة وعامل الخدمة.

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

في هذا الدليل، سنستكشف الطرق المختلفة لتنفيذ اتصالات ثنائية الاتجاه بين Window وسياق service worker ، وذلك من خلال استكشاف مكتبة Workbox وواجهات برمجة التطبيقات المختلفة، بالإضافة إلى بعض الحالات المتقدّمة.

مخطّط بياني يعرض مشغّل الخدمات والصفحة تتبادل الرسائل

استخدام Workbox

workbox-window هي مجموعة من وحدات مكتبة Workbox المصمّمة لتشغيلها في سياق النوافذ. توفّر فئة 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);

ينفِّذ worker الخدمة مستمعًا للرسائل في الطرف الآخر، ويستجيب لworker الخدمة المسجَّل:

const SW_VERSION = '1.0.0';

self.addEventListener('message', (event) => {
  if (event.data.type === 'GET_VERSION') {
    event.ports[0].postMessage(SW_VERSION);
  }
});

في الواقع، تستخدم المكتبة واجهة برمجة تطبيقات للمتصفّح سنراجعها في القسم التالي: Message Channel، ولكنها تلخّص العديد من تفاصيل التنفيذ، ما يسهّل استخدامها، مع الاستفادة من التوافق الواسع النطاق مع المتصفّحات الذي توفّره واجهة برمجة التطبيقات هذه.

مخطّط بياني يعرض تواصلاً متبادلاً بين الصفحة وعامل الخدمة، باستخدام نافذة Workbox

استخدام واجهات برمجة تطبيقات المتصفّح

إذا لم تكن مكتبة Workbox كافية لتلبية احتياجاتك، تتوفر العديد من واجهات برمجة التطبيقات ذات المستوى الأدنى لتنفيذ الاتصال "الثنائي الاتجاه" بين الصفحات وعاملي الخدمة. هناك بعض أوجه التشابه والاختلاف بينهما:

أوجه التشابه:

  • في جميع الحالات، يبدأ الاتصال من جهة واحدة عبر واجهة postMessage() ويتم تلقّيه على الطرف الآخر من خلال تنفيذ معالِج message.
  • من الناحية العملية، تسمح لنا جميع واجهات برمجة التطبيقات المتاحة بتنفيذ حالات الاستخدام نفسها، ولكن قد يؤدي استخدام بعضها إلى تبسيط عملية التطوير في بعض السيناريوهات.

الاختلافات:

  • لديهم طرق مختلفة لتحديد الطرف الآخر من التواصل: يستخدم بعضها إشارة صريحة إلى السياق الآخر، بينما يمكن للبعض الآخر التواصل ضمنيًا عن طريق كائن وكيل يتم إنشاء مثيل له على كل جانب.
  • ويختلف توافق المتصفح من حيث لآخر.
مخطّط بياني يعرض التواصل المتبادل بين الصفحة وعامل الخدمة وواجهات برمجة التطبيقات المتاحة للمتصفّح

واجهة برمجة التطبيقات Broadcast Channel API

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

  • Chrome: 54
  • ‫Edge: 79
  • Firefox: 38.
  • ‫Safari: 15.4

المصدر

تتيح 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...
  }
};

كما هو موضّح، لا تتضمّن الرسالة إشارة صريحة إلى سياق معيّن، لذا لا حاجة إلى الحصول على إشارة أولاً إلى عامل الخدمة أو أي عميل معيّن.

مخطّط بياني يعرض الاتصال ثنائي الاتجاه بين مشغّل الصفحة ومشغّل الخدمات، باستخدام كائن قناة بث

السلبيات هي أنّه في وقت كتابة هذا التقرير، كانت واجهة برمجة التطبيقات متوافقة مع Chrome وFirefox وEdge، إلا أنّ المتصفحات الأخرى، مثل Safari، لا تتوافق معها بعد.

Client API

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

  • Chrome:‏ 40
  • ‫Edge: 17
  • Firefox: 44
  • ‫Safari: 11.1

المصدر

تتيح لك 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'});
  }
});
مخطّط بياني يعرض عامل خدمة يتواصل مع مجموعة من العملاء

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

قناة الرسائل

دعم المتصفح

  • Chrome: 2-
  • Edge: 12.
  • Firefox: 41.
  • Safari: 5.

المصدر

تتطلّب قناة الرسائل تحديد منفذ وإرساله من سياق إلى آخر لإنشاء قناة اتصال ثنائي الاتجاه.

لبدء القناة، تنشئ الصفحة مثيلًا لعنصر 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
};
مخطّط بياني يعرض صفحة تمرّ منفذًا إلى مشغّل الخدمات لإنشاء اتصال ثنائي الاتجاه

يتلقّى العامل في الخدمة المنفذ ويحفظ مرجعًا إليه ويستخدمه لإرسال رسالة إلى الجانب الآخر:

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 لتخزينها مؤقتًا من سياق إلى آخر. في هذا القسم، سنستكشف وجهتَي برمجة تطبيقات للتعامل مع سيناريوهَين محدّدَين: عدم توفّر اتصال بالإنترنت وعمليات التنزيل التي تستغرق وقتًا طويلاً.

المزامنة في الخلفية

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

  • Chrome: 49
  • الحافة: 79.
  • Firefox: غير متوافق
  • Safari: غير متوافق

المصدر

قد يريد تطبيق المحادثة التأكّد من عدم فقدان الرسائل أبدًا بسبب ضعف الاتصال. وتتيح لك واجهة برمجة تطبيقات Background Sync تأجيل الإجراءات لإعادة المحاولة عندما يكون لدى المستخدم اتصال ثابت. ويُعدّ ذلك مفيدًا لضمان إرسال أي محتوى يريده المستخدم.

بدلاً من الواجهة 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 ميزة "المزامنة في الخلفية" للحفاظ على الاستعلامات التي تعذّر إجراؤها بسبب ضعف الاتصال بالإنترنت، وإعادة محاولة إجرائها لاحقًا عندما يكون المستخدم متصلاً بالإنترنت. بعد تنفيذ العملية، يُعلم التطبيق المستخدم بالنتيجة من خلال إشعار فوري على الويب:

مخطّط بياني يعرض صفحة تمرّر منفذًا إلى عامل خدمة لإنشاء اتصال ثنائي الاتجاه

استرجاع البيانات في الخلفية

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

  • Chrome: 74
  • ‫Edge: 79
  • Firefox: غير متوافق
  • Safari: غير متوافق

المصدر

بالنسبة إلى المهام القصيرة نسبيًا، مثل إرسال رسالة أو قائمة بعناوين 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}%`);
});
مخطّط بياني يعرض صفحة تمرّر منفذًا إلى عامل خدمة لإنشاء اتصال ثنائي الاتجاه
يتم تعديل واجهة المستخدم للإشارة إلى مستوى تقدّم عملية التنزيل (على يمين الشاشة). بفضل عاملي الخدمة، يمكن متابعة تشغيل العملية عند إغلاق جميع علامات التبويب (إلى اليمين).

الخطوات التالية

في هذا الدليل، استكشَفنا الحالة الأكثر عمومية للتواصل بين عمال الخدمة والصفحات (التواصل الثنائي الاتجاه).

في كثير من الأحيان، قد يحتاج أحدهما إلى سياق واحد فقط للتواصل مع الآخر، بدون تلقّي ردّ. اطّلِع على الأدلة التالية للحصول على إرشادات حول كيفية تنفيذ أساليب أحادية الاتجاه في صفحاتك من مشغّل الخدمات وإليه، بالإضافة إلى حالات الاستخدام وأمثلة الإنتاجية:

  • دليل التخزين المؤقت الضمني: استدعاء عامل خدمة من الصفحة لتخزين الموارد مؤقتًا مسبقًا (على سبيل المثال، في سيناريوهات الجلب المسبق).
  • تحديثات البث: الاتصال بالصفحة من عامل الخدمة لإبلاغه بالتحديثات المهمة (على سبيل المثال، توفُّر إصدار جديد من تطبيق الويب)