Dari waktu ke waktu, pengembang perlu menjalin komunikasi antara beberapa tab browser untuk dapat mengirim pesan dari satu tab ke tab lainnya dan menerima tanggapan. Kami juga menghadapi kebutuhan ini di beberapa titik.
Beberapa solusi sudah ada (seperti, misalnya, BroadcastChannel API). Namun, dukungan perambannya banyak yang diinginkan , jadi kami memutuskan untuk menggunakan perpustakaan kami sendiri. Ketika perpustakaan sudah siap, fungsi itu tidak lagi diperlukan. Namun demikian, tugas lain muncul: komunikasi antara iframe dan jendela utama.
Pada pemeriksaan lebih dekat, ternyata dua pertiga perpustakaan tidak perlu diubah - hanya beberapa kode refactoring yang diperlukan. Perpustakaan adalah PROTOCOL komunikasi yang dapat bekerja dengan data teks. Itu dapat diterapkan dalam semua kasus di mana teks ditransfer, seperti iframes, window.open, pekerja, tab browser atau WebSocket.
Bagaimana cara kerjanya
Saat ini, protokol memiliki dua fungsi: mengirim pesan dan berlangganan acara. Pesan apa pun dalam protokol adalah objek data. Bagi kami, bidang utama dalam objek itu adalah tipe , yang memberi tahu kami seperti apa pesannya. Bidang jenis adalah enum dengan nilai-nilai berikut:
- 0 - mengirim pesan
- 1 - mengirim permintaan
- 2 - menerima respons.
Mengirim pesan
Mengirim pesan tidak menyiratkan respons. Untuk mengirim pesan, kami membuat objek dengan bidang-bidang berikut:
- tipe - tipe acara 0
- nama - nama acara pengguna
- data - data pengguna (seperti JSON).
Saat menerima pesan di sisi lain dengan jenis bidang = 0 , kita tahu itu adalah peristiwa, dengan nama dan data acara yang ada. Yang harus kita lakukan adalah menyiarkan acara (hampir pola EventEmitter standar).
Cara kerjanya dalam skema sederhana:

Mengirim permintaan
Mengirim permintaan menyiratkan bahwa di dalam perpustakaan, id permintaan dibuat dan perpustakaan akan menunggu jawaban dengan id . Setelah berhasil menerima respons, semua bidang bantu akan dihapus darinya, dan respons itu akan dikembalikan kepada pengguna. Juga, waktu respons maksimum dapat diatur.

Adapun permintaan, ini sedikit lebih rumit. Untuk menanggapi permintaan, Anda harus mengumumkan metode yang tersedia dalam protokol kami. Ini dilakukan dengan metode registerRequestHandler . Itu menerima nama permintaan untuk respons dan fungsi yang mengembalikan respons. Untuk membuat permintaan, kita memerlukan id , dan pada dasarnya kita bisa menggunakan cap waktu , tetapi tidak nyaman untuk menyesuaikan. Jadi, ini adalah ID kelas yang mengirim respons + nomor respons + string literal. Sekarang kita membuat objek dengan bidang-bidang berikut: id , ketik = 1 , nama sebagai nama permintaan dan data sebagai data pengguna (seperti JSON).
Saat menerima permintaan, kami memeriksa apakah kami memiliki API untuk menanggapi permintaan ini, dan jika tidak, kami mengembalikan pesan kesalahan. Jika kami memiliki API, kami mengembalikan hasil dari menjalankan fungsi dari registerRequestHandler , dengan nama permintaan masing-masing.
Untuk respons, kami membuat objek dengan bidang: ketik = 2, id sebagai ID dari pesan yang kami balas, berstatus sebagai bidang yang mengatakan jika respons ini adalah kesalahan (jika kami tidak memiliki API, atau pawang mengalami kesalahan, atau pengguna mengembalikan janji yang ditolak, atau kesalahan lain terjadi (serial)), dan konten sebagai data respons.
Jadi, kami telah menjelaskan operasi protokol, yang mengeksekusi kelas Bus tetapi belum menjelaskan proses pengiriman dan penerimaan pesan. Untuk itu, kita membutuhkan adaptor kelas dengan tiga metode.
- send adalah metode yang pada dasarnya bertanggung jawab untuk mengirim pesan
- addListener adalah metode untuk berlangganan acara
- destroy adalah metode untuk menghapus langganan saat menghapus Bus .
Adaptor Eksekusi protokol
Untuk meluncurkan protokol, saat ini, hanya adaptor untuk bekerja dengan iframe / jendela yang siap. Ini menggunakan postMessage dan addEventListener . Ini cukup mudah: Anda perlu mengirim pesan ke postMessage dengan asal yang benar dan mendengarkan pesan melalui addEventListener pada acara "pesan" .
Kami menemukan beberapa nuansa:
- Anda harus selalu mendengarkan tanggapan di jendela ANDA dan mengirimkannya di jendela LAINNYA (iframe, pembuka, orang tua, pekerja, dll.). Jika Anda mencoba mendengarkan pesan di jendela LAINNYA dan asal berbeda dari yang sekarang, kesalahan akan terjadi.
- Saat menerima pesan, pastikan pesan itu ditujukan kepada Anda: jendela dapat mengakomodasi banyak pesan analitik, WebStorm (jika Anda menggunakannya) dan iframe lainnya, jadi Anda harus yakin acara tersebut ada dalam protokol Anda dan ditujukan untuk Anda.
- Anda tidak dapat mengembalikan janji dengan salinan Window , karena janji ketika mengembalikan hasil, akan mencoba memeriksa apakah hasilnya memiliki metode itu. Jika Anda tidak memiliki akses ke jendela (misalnya, jendela dengan asal lain), kesalahan akan terjadi (meskipun tidak di semua browser). Untuk menghindari masalah ini, cukup membungkus jendela pada objek dan memasukkan objek ke dalam janji yang memiliki tautan ke jendela yang benar.
Contoh penggunaan:
Perpustakaan tersedia dalam NPM dan Anda dapat dengan mudah menginstalnya melalui manajer paket Anda - @ Wave / Wave-Browser-Bus
Untuk menjalin komunikasi dua arah dengan iframe, cukup menggunakan kode ini:
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', () => {
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
Kami memiliki protokol yang fleksibel dan serbaguna yang dapat digunakan dalam situasi apa pun. Selanjutnya, saya berencana untuk memisahkan adapter dari protokol dan memasukkannya ke dalam paket npm yang terpisah, dan menambahkan adapter untuk tab pekerja dan browser. Saya ingin menulis adaptor yang menjalankan protokol untuk tujuan lain semudah mungkin. Jika Anda ingin bergabung dengan proses pengembangan atau memiliki ide mengenai fungsi perpustakaan, Anda dipersilakan untuk menghubungi repo .