من وقت لآخر ، يحتاج المطورون إلى إنشاء اتصال بين العديد من علامات تبويب المتصفح لتكون قادرًا على إرسال رسائل من علامة تبويب إلى أخرى وتلقي الردود. لقد واجهنا هذه الحاجة أيضًا في مرحلة ما.
توجد بالفعل بعض الحلول (مثل ، BroadcastChannel API). ومع ذلك ، فإن دعم المتصفح الخاص به يترك الكثير مما هو مرغوب فيه ، لذلك قررنا استخدام مكتبتنا الخاصة. عندما كانت المكتبة جاهزة ، لم تعد هذه الوظيفة مطلوبة. ومع ذلك ، ظهرت مهمة أخرى: التواصل بين iframe والنافذة الرئيسية.
بعد الفحص الدقيق ، تبين أنه لا يتعين تغيير ثلثي المكتبة - كان من الضروري فقط إعادة هيكلة بعض الرموز. المكتبة عبارة عن بروتوكول اتصال يمكنه العمل مع البيانات النصية. يمكن تطبيقه في جميع الحالات التي يتم فيها نقل النص ، مثل iframe أو window.open أو العامل أو علامات تبويب المتصفح أو WebSocket.
كيف يعمل
يحتوي البروتوكول حاليًا على وظيفتين: إرسال الرسائل والاشتراك في الأحداث. أي رسالة في البروتوكول هي كائن البيانات. بالنسبة لنا ، الحقل الرئيسي في هذا الكائن هو النوع ، الذي يخبرنا بنوع الرسالة. حقل الكتابة عبارة عن تعداد بالقيم التالية:
- 0 - إرسال رسالة
- 1 - إرسال الطلب
- 2 - تلقي استجابة.
إرسال رسالة
لا يعني إرسال رسالة استجابة. لإرسال رسالة ، نقوم بإنشاء كائن مع الحقول التالية:
- النوع - نوع الحدث 0
- اسم - اسم الحدث المستخدم
- البيانات - بيانات المستخدم (مثل JSON).
عند تلقي رسالة على الجانب الآخر مع حقل الكتابة = 0 ، نعلم أنها حدث ، له اسم وبيانات حدث حالية. كل ما يتعين علينا القيام به هو بث الحدث (تقريبًا نمط EventEmitter القياسي).
كيف يعمل في مخطط بسيط:

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

بالنسبة للطلبات ، هذا الأمر أكثر تعقيدًا. للرد على طلب ما ، يجب عليك الإعلان عن الطرق المتاحة في بروتوكولنا. يتم ذلك باستخدام أسلوب registerRequestHandler . يقبل اسم طلب استجابة ووظيفة ترجع الاستجابة. لإنشاء طلب ، نحتاج إلى معرف ، ويمكننا استخدام الطابع الزمني بشكل أساسي ، لكن ليس من المناسب ضبطه. لذلك ، هذا هو معرف الفصل الذي يرسل رقم استجابة + استجابة + سلسلة حرفية. الآن نقوم بإنشاء كائن مع الحقول التالية: id ، نوع = 1 ، الاسم كاسم الطلب والبيانات كبيانات المستخدم (مثل JSON).
عند استلام الطلب ، نتحقق مما إذا كان لدينا واجهة برمجة تطبيقات للاستجابة لهذا الطلب ، وإذا لم نفعل ذلك ، فنحن نرجع رسالة خطأ. إذا كان لدينا واجهة برمجة تطبيقات ، فإننا نرجع نتيجة تنفيذ الوظيفة من registerRequestHandler ، مع اسم الطلب المعني.
للاستجابة ، نقوم بإنشاء كائن مع الحقول: type = 2 ، id كمعرّف الرسالة التي نستجيب لها ، والحالة كحقل يوضح إذا كانت هذه الاستجابة خطأ (إذا لم يكن لدينا واجهة برمجة تطبيقات ، أو حدث خطأ في المعالج ، أو قام المستخدم بإرجاع وعد مرفوض ، أو حدث خطأ آخر (التسلسل)) ، والمحتوى كبيانات استجابة.
لذلك ، لقد وصفنا تشغيل البروتوكول ، الذي ينفذ فئة الحافلات ولكن لم يوضح عملية إرسال واستقبال الرسائل. لذلك ، نحن بحاجة إلى محولات فئة بثلاثة طرق.
- إرسال هي طريقة مسؤولة أساسا عن إرسال الرسائل
- addListener هي طريقة للاشتراك في الأحداث
- إتلاف طريقة حذف الاشتراكات عند حذف Bus .
المحولات. تنفيذ البروتوكول
لبدء تشغيل البروتوكول ، حاليًا ، فقط المحول للعمل مع iframe / window جاهز. ويستخدم postMessage و addEventListener . إنه أمر واضح ومباشر: تحتاج إلى إرسال رسالة إلى postMessage بأصل صحيح والاستماع إلى الرسائل عبر addEventListener في حدث "message" .
واجهنا بعض الفروق الدقيقة:
- يجب عليك دائمًا الاستماع إلى الردود في نافذتك وإرسالها في نافذة أخرى (iframe ، وفتاحة ، والد ، عامل ، إلخ). إذا حاولت الاستماع إلى رسالة على نافذة أخرى وكان الأصل يختلف عن الرسالة الحالية ، فسيحدث خطأ.
- عند تلقي رسالة ، تأكد من توجيهها إليك: يمكن أن تستوعب النافذة العديد من رسائل التحليلات ، WebStorm (إذا كنت تستخدمها) وغيرها من iframe ، لذلك يجب أن تكون على يقين من أن الحدث موجود في البروتوكول الخاص بك ومخصص لك.
- لا يمكنك إرجاع وعد باستخدام نسخة من النافذة ، لأن الوعد عند إرجاع النتيجة ، سيحاول التحقق مما إذا كانت النتيجة تحتوي على طريقة وقتها . إذا لم يكن لديك وصول إلى النافذة (على سبيل المثال ، نافذة من أصل آخر) ، فسيحدث خطأ (على الرغم من أنه ليس في جميع المتصفحات). لتجنب هذه المشكلة ، يكفي التفاف النافذة في الكائن ووضع كائن في الوعد الذي له رابط إلى النافذة الصحيحة.
أمثلة الاستخدام:
تتوفر المكتبة في NPM ويمكنك تثبيتها بسهولة عبر مدير الحزم - @ waves / waves-browser-bus
لإنشاء اتصال ثنائي الاتجاه مع iframe ، يكفي استخدام هذا الرمز:
import { Bus, WindowAdapter } from '@waves/waves-browser-bus'; const url = 'https://some-iframe-content-url.com'; const iframe = document.createElement('iframe'); WindowAdapter.createSimpleWindowAdapter(iframe).then(adapter => { const bus = new Bus(adapter); bus.once('ready', () => {
iframe داخل:
import { Bus, WindowAdapter } from '@waves/waves-browser-bus'; WindowAdapter.createSimpleWindowAdapter().then(adapter => { const bus = new Bus(adapter); bus.dispatchEvent('ready', null);
ما التالي؟
لدينا بروتوكول مرن متعدد الاستخدامات يمكن استخدامه في أي موقف. بعد ذلك ، أخطط لفصل المحولات عن البروتوكول ووضعها في حزم npm منفصلة ، وإضافة محولات لعلامات تبويب العامل والمتصفح. أريد أن تكون محولات الكتابة التي تنفذ البروتوكول لأي أغراض أخرى سهلة قدر الإمكان. إذا كنت ترغب في الانضمام إلى عملية التطوير أو لديك أفكار تتعلق بوظيفة المكتبة ، فنحن نرحب بك للاتصال في الريبو .