بروتوكول للاتصال بين iframe ونافذة المتصفح الرئيسية

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


هناك حلول قياسية مثل 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 }); }); iframe.src = url; //   url   WindowAdapter.createSimpleWindowAdapter document.body.appendChild(iframe); 

وداخل iframe:


 import { Bus, WindowAdapter } from '@waves/waves-browser-bus'; WindowAdapter.createSimpleWindowAdapter().then(adapter => { const bus = new Bus(adapter); bus.dispatchEvent('ready', null); //      }); 

ما التالي؟


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


إذا كنت ترغب في الانضمام إلى التطوير أو الأفكار المتعلقة بوظيفة المكتبة ، فأنت مرحب بك في المستودع .

Source: https://habr.com/ru/post/ar455942/


All Articles