Protokol untuk komunikasi antara iframe dan jendela browser utama

Banyak pengembang secara berkala perlu menjalin komunikasi antara beberapa tab browser: kemampuan untuk mengirim pesan dari satu ke yang lain dan menerima respons. Tugas semacam itu muncul di hadapan kita.


Ada solusi standar seperti BroadcastChannel, tetapi dukungan browser sekarang banyak yang diinginkan , jadi kami memutuskan untuk mengimplementasikan perpustakaan kami. Ketika perpustakaan siap, ternyata fungsi tersebut tidak lagi diperlukan, tetapi tugas lain muncul: perlu untuk berkomunikasi antara iframe dan jendela utama.


Setelah diperiksa lebih dekat, ternyata dua pertiga perpustakaan tidak dapat diubah secara bersamaan, Anda hanya perlu sedikit memperbaiki kode. Perpustakaan lebih merupakan PROTOCOL komunikasi yang dapat bekerja dengan data teks. Ini dapat digunakan dalam semua kasus jika dimungkinkan untuk mentransfer teks (iframe, window.open, pekerja, tab browser, WebSocket).


Bagaimana cara kerjanya


Saat ini, protokol memiliki dua fungsi: mengirim pesan dan berlangganan acara. Pesan apa pun dalam protokol adalah objek dengan data. Bidang utama objek ini adalah bidang jenis , yang memberi tahu kita seperti apa pesannya. Bidang jenis adalah enum dengan nilai:


  • 0 - kirim pesan
  • 1 - mengirim permintaan
  • 2 - menerima respons.

Pengiriman pesan


Mengirim pesan tidak menyiratkan respons. Untuk mengirim acara, kami membuat objek dengan bidang:


  • tipe - tipe acara 0
  • nama - nama acara pengguna
  • data - data pengguna (seperti JSON).

Ketika kami menerima pesan di sisi lain dengan bidang tipe = 0, kami tahu bahwa ini adalah acara dan ada nama acara dan data. Yang tersisa hanyalah menjalankan event (pola EventEmitter yang hampir teratur).


Skema bekerja dengan acara:



Permintaan Pengajuan


Mengirim permintaan menyiratkan bahwa ID permintaan dihasilkan di dalam perpustakaan, perpustakaan akan menunggu respons dengan ID ini, dan setelah bidang layanan respons yang berhasil akan dihapus darinya, dan respons akan dikembalikan kepada pengguna. Selain itu, Anda dapat mengatur waktu respons maksimum.



Dengan permintaan itu, semuanya agak lebih rumit. Untuk menanggapi permintaan, Anda harus mendeklarasikan metode yang tersedia dalam protokol kami. Ini dilakukan dengan menggunakan metode registerRequestHandler . Ia menerima nama permintaan yang akan ditanggapi, dan fungsi yang mengembalikan respons. Untuk membuat permintaan, kami memerlukan id , dan secara umum, Anda dapat menggunakan stempel waktu , tetapi sangat tidak nyaman untuk melakukan debug. Oleh karena itu, ini adalah id dari kelas yang mengirimkan permintaan + nomor seri dari permintaan + string konstan. Selanjutnya, kita membangun objek dengan id bidang, ketik - dengan nilai 1, nama - nama permintaan, data - data pengguna (seperti JSON).


Setelah menerima permintaan, kami memeriksa apakah kami memiliki API untuk menanggapi permintaan ini, jika tidak ada API, kami mengembalikan kesalahan. Jika ada API, kami mengembalikan hasil fungsi dari registerRequestHandler , dengan nama permintaan yang sesuai.


Objek dibentuk untuk respons dengan bidang tipe - dengan nilai 2, id - id pesan yang kami tanggapi, status - bidang yang mengatakan apakah respons ini adalah kesalahan (jika tidak ada API, atau kesalahan yang terjadi di pintu keluar pengguna, atau pengguna yang dikembalikan Janji yang Ditolak, kesalahan lain (cerita bersambung)), konten - data respons.


Jadi, kami menggambarkan operasi protokol itu sendiri, yang mengimplementasikan kelas Bus , tetapi tidak menjelaskan bagaimana sebenarnya mengirim dan menerima pesan. Untuk ini kita membutuhkan adaptor - kelas dengan 3 metode:


  • send - metode yang sebenarnya bertanggung jawab untuk mengirim pesan
  • addListener - metode untuk berlangganan acara
  • menghancurkan - untuk menghancurkan langganan ketika menghancurkan Bus.

Adaptor Implementasi protokol.


Untuk memulai semua ini, saat ini hanya adaptor untuk bekerja dengan iframe / jendela yang siap. Ini berfungsi pada postMessage dan addEventListener . Semuanya cukup sederhana di sini: Anda perlu mengirim pesan ke postMessage dengan asal yang benar dan mendengarkan pesan melalui addEventListener pada acara "message".


Kehalusan kecil yang kami temui:


  • Anda harus selalu mendengarkan jawaban di jendela ANDA, dan mengirimkannya ke orang lain (iframe, pembuka, orang tua, pekerja, ...).
    Faktanya adalah ketika Anda mencoba mendengarkan pesan di jendela orang lain, jika asal berbeda dari yang sekarang, kesalahan akan terjadi.
  • Ketika Anda menerima pesan, pastikan itu dikirim kepada Anda (banyak pesan dari analytics dipicu pada jendela,
    WebStrom (jika Anda menggunakannya), iframe asing, jadi Anda harus memastikan bahwa acara tersebut ada dalam protokol kami dan untuk kami).
  • Anda tidak dapat mengembalikan Janji dengan turunan Window , karena Janji, saat mengembalikan hasil, mencoba memeriksa apakah hasilnya memiliki metode, dan jika Anda tidak memiliki akses ke jendela (misalnya, jendela dengan asal yang berbeda), kesalahan akan terjadi (walaupun tidak di semua browser ) Untuk menghindari masalah ini, cukup bungkus jendela di suatu objek dan masukkan objek Janji di mana ada tautan ke jendela yang diinginkan.

Contoh penggunaan:


Perpustakaan dapat diinstal menggunakan manajer paket favorit Anda - @ Wave / Wave-browser-bus


Untuk membangun komunikasi dua arah dengan iframe, cukup tulis kode:


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); 

Dan di dalam iframe:


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

Apa selanjutnya


Ternyata protokol yang fleksibel dan universal yang dapat digunakan dalam situasi apa pun.
Sekarang saya berencana untuk memisahkan adapter dari protokol dan menempatkannya dalam paket npm terpisah, menambahkan adapter untuk bekerja dengan tab pekerja dan browser. Saya ingin menulis adaptor yang mengimplementasikan protokol untuk kebutuhan lain, sesederhana mungkin.


Jika Anda memiliki keinginan untuk bergabung dengan pengembangan atau ide-ide tentang fungsi perpustakaan - Anda dipersilakan ke repositori .

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


All Articles