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

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

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

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

الاختلافات:

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

واجهة برمجة التطبيقات 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 من خلال تنفيذ methods مثل 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 حاليًا في جميع المتصفحات الكبرى.

واجهات برمجة التطبيقات المتقدّمة: المزامنة في الخلفية وميزة "جلب البيانات في الخلفية"

في هذا الدليل، استكشَفنا طُرق تنفيذ تقنيات التواصل المتبادل، للحالات ال relativamente بسيطة، مثل تمرير رسالة سلسلة تصف العملية التي سيتم تنفيذها، أو قائمة بعناوين URL للتخزين المؤقت من سياق إلى آخر. في هذا القسم، سنستكشف وجهتَي برمجة تطبيقات للتعامل مع سيناريوهَين محدّدَين: عدم توفّر اتصال بالإنترنت وعمليات التنزيل التي تستغرق وقتًا طويلاً.

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

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

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

المصدر

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

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

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

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

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

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