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

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

مع الطلب ، كل شيء أكثر تعقيدًا إلى حد ما. للرد على طلب ما ، يجب أن تعلن الطرق المتاحة في بروتوكولنا. يتم ذلك باستخدام طريقة registerRequestHandler . يقبل اسم الطلب الذي سيرد عليه ، والدالة التي تُرجع الاستجابة. لإنشاء طلب ، نحتاج إلى معرف ، وبصفة عامة ، يمكنك استخدام الطابع الزمني ، لكن من غير المناسب للغاية تصحيح الأخطاء. لذلك ، هذا هو معرف الفئة الذي يرسل الطلب + الرقم التسلسلي لقيمة الطلب + السلسلة. بعد ذلك ، نقوم ببناء كائن مع معرف الحقول ، اكتب - بقيمة 1 ، الاسم - اسم الطلب ، البيانات - بيانات المستخدم (مثل JSON-like).
عند استلام الطلب ، نتحقق مما إذا كان لدينا واجهة برمجة تطبيقات للرد على هذا الطلب ، وإذا لم يكن هناك واجهة برمجة تطبيقات ، نرجع خطأ. إذا كان هناك API ، نرجع نتيجة الدالة من registerRequestHandler ، مع اسم الطلب المقابل.
يتم تكوين كائن للاستجابة مع حقول الكتابة - بقيمة 2 ، معرف الهوية للرسالة التي نستجيب لها ، الحالة - حقل يوضح ما إذا كانت هذه الاستجابة خطأ (في حالة عدم وجود واجهة برمجة تطبيقات ، أو حدث خطأ في خروج المستخدم ، أو قام المستخدم بإرجاع "رفض مرفوض" ، أخطاء أخرى (إجراء تسلسل)) ، بيانات استجابة المحتوى .
وهكذا ، وصفنا تشغيل البروتوكول نفسه ، الذي ينفذ فئة الناقل ، لكننا لم نصف كيفية إرسال واستقبال الرسائل بالفعل. لهذا نحتاج إلى محولات - فئة بها 3 طرق:
- إرسال - طريقة مسؤولة فعليًا عن إرسال رسالة
- addListener - طريقة للاشتراك في الأحداث
- تدمير - لتدمير الاشتراكات عند تدمير حافلة.
المحولات. تنفيذ البروتوكول.
لبدء كل هذا ، في الوقت الحالي فقط المحول للعمل مع iframe / window جاهز. وهو يعمل على postMessage و addEventListener . كل شيء بسيط للغاية هنا: تحتاج إلى إرسال رسالة إلى postMessage مع الأصل الصحيح والاستماع إلى الرسائل من خلال addEventListener على "رسالة" الحدث.
التفاصيل الدقيقة التي واجهناها:
- يجب عليك دائمًا الاستماع إلى الإجابات في نافذتك ، وإرسالها إلى شخص آخر (iframe ، وفتاحة ، والد ، عامل ، ...).
الحقيقة هي أنه عند محاولة الاستماع إلى رسالة على نافذة شخص آخر ، إذا كان الأصل يختلف عن الرسالة الحالية ، فسيحدث خطأ. - عندما تتلقى رسالة ، تأكد من إرسالها إليك (يتم تشغيل مجموعة من الرسائل من التحليلات في النافذة ،
WebStrom (إذا كنت تستخدمه) ، iframe الأجنبية ، لذلك يجب عليك التأكد من أن الحدث في بروتوكولنا ولنا). - لا يمكنك إرجاع Promise مع مثيل لـ Window ، حيث إن Promise ، عند إرجاع النتيجة ، يحاول التحقق مما إذا كانت النتيجة لها طريقة ، وإذا لم يكن لديك إمكانية الوصول إلى النافذة (نافذة ذات أصل مختلف ، على سبيل المثال) ، سيحدث خطأ (على الرغم من عدم وجوده في جميع المتصفحات ). لتجنب هذه المشكلة ، ما عليك سوى لف النافذة في كائن ووضعها في كائن Promise الذي يوجد به رابط للإطار المطلوب.
أمثلة على الاستخدام:
يمكن تثبيت المكتبة باستخدام مدير الحزم المفضل لديك - @ 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 منفصلة ، وإضافة محولات للعمل مع علامات تبويب العامل والمتصفح. أود أن أكتب محولات تنفذ البروتوكول لأي احتياجات أخرى ، كان الأمر بسيطًا قدر الإمكان.
إذا كنت ترغب في الانضمام إلى التطوير أو الأفكار المتعلقة بوظيفة المكتبة ، فأنت مرحب بك في المستودع .