Komunikasi antar tab atau windows

176

Saya sedang mencari cara bagaimana berkomunikasi antara banyak tab atau jendela di browser (pada domain yang sama, bukan CORS) tanpa meninggalkan jejak. Ada beberapa solusi:

  1. menggunakan objek jendela
  2. postMessage
  3. kue
  4. penyimpanan lokal

Yang pertama mungkin adalah solusi terburuk - Anda perlu membuka jendela dari jendela Anda saat ini dan kemudian Anda dapat berkomunikasi hanya selama Anda tetap membuka jendela. Jika Anda memuat ulang halaman di salah satu jendela, kemungkinan besar Anda kehilangan komunikasi.

Pendekatan kedua, menggunakan postMessage, mungkin memungkinkan komunikasi lintas asal, tetapi mengalami masalah yang sama dengan pendekatan pertama. Anda perlu mempertahankan objek jendela.

Cara ketiga, menggunakan cookie, menyimpan beberapa data di browser, yang secara efektif dapat terlihat seperti mengirim pesan ke semua jendela di domain yang sama, tetapi masalahnya adalah Anda tidak akan pernah tahu apakah semua tab membaca "pesan" sudah atau belum sebelumnya membersihkan. Anda harus menerapkan semacam batas waktu untuk membaca cookie secara berkala. Selain itu, Anda dibatasi oleh panjang cookie maksimum, yaitu 4KB.

Solusi keempat, menggunakan localStorage, tampaknya mengatasi keterbatasan cookie, dan bahkan dapat mendengarkan-menggunakan acara. Cara menggunakannya dijelaskan dalam jawaban yang diterima.

Sunting 2018: jawaban yang diterima masih berfungsi, tetapi ada solusi yang lebih baru untuk peramban modern, untuk menggunakan BroadcastChannel. Lihat jawaban lain untuk contoh sederhana yang menjelaskan cara mudah mengirim pesan antar tab dengan menggunakan BroadcastChannel.

Tomas M
sumber
4
Mengapa pertanyaan ini ditutup "terlalu luas" ketika pertanyaan yang hampir sama telah terbuka selama bertahun-tahun? Mengirim pesan ke semua jendela / tab terbuka menggunakan JavaScript , stackoverflow.com/questions/2236828/… , Bagaimana Anda berkomunikasi antara 2 tab / windows browser? dan beberapa lagi.
Dan Dascalescu
Saya membuat perpustakaan di atas Penyimpanan lokal dan sessionStorage untuk mengelola penyimpanan data sisi klien. Anda dapat melakukan hal-hal seperti storageManager.savePermanentData ('data', 'key'); atau storageManager.saveSyncedSessionData ('data', 'key'); berdasarkan bagaimana Anda ingin data Anda berperilaku. Ini sangat menyederhanakan proses. Artikel lengkap di sini: ebenmonney.com/blog/…
adentum
2
Saya telah membuat perpustakaan sysend.js beberapa tahun yang lalu, dalam versi terbaru menggunakan BroadcastChannel. Anda dapat menguji perpustakaan dengan membuka halaman ini dua kali jcubic.pl/sysend.php , itu juga berfungsi dengan asal yang berbeda jika Anda memberikan proxy iframe.
jcubic
Apakah saya menganggap sub domain sebagai asal yang sama? Maksud saya, saya memiliki di bawah tiga domain, apakah mereka berkomunikasi melalui broadcastchannel api? alpha.firstdomain.com, beta.firstdomain.com, gama.firstdomain.com
Tejas Patel

Jawaban:

142

Sunting 2018: Anda sebaiknya menggunakan BroadcastChannel untuk tujuan ini, lihat jawaban lain di bawah ini. Namun jika Anda masih lebih suka menggunakan penyimpanan lokal untuk komunikasi antar tab, lakukan seperti ini:

Untuk mendapat pemberitahuan saat tab mengirim pesan ke tab lain, Anda hanya perlu mengikat acara 'penyimpanan'. Di semua tab, lakukan ini:

$(window).on('storage', message_receive);

Fungsi message_receiveakan dipanggil setiap kali Anda menetapkan nilai localStorage di tab lain. Pendengar acara juga berisi data yang baru saja disetel ke localStorage, jadi Anda bahkan tidak perlu menguraikan objek localStorage itu sendiri. Ini sangat berguna karena Anda dapat mengatur ulang nilai tepat setelah ditetapkan, untuk membersihkan jejak secara efektif. Berikut adalah fungsi untuk olahpesan:

// use local storage for messaging. Set message in local storage and clear it right away
// This is a safe way how to communicate with other tabs while not leaving any traces
//
function message_broadcast(message)
{
    localStorage.setItem('message',JSON.stringify(message));
    localStorage.removeItem('message');
}


// receive message
//
function message_receive(ev)
{
    if (ev.originalEvent.key!='message') return; // ignore other keys
    var message=JSON.parse(ev.originalEvent.newValue);
    if (!message) return; // ignore empty msg or msg reset

    // here you act on messages.
    // you can send objects like { 'command': 'doit', 'data': 'abcd' }
    if (message.command == 'doit') alert(message.data);

    // etc.
}

Jadi sekarang setelah tab Anda mengikat acara onstorage, dan Anda memiliki dua fungsi ini diimplementasikan, Anda dapat dengan mudah menyiarkan pesan ke panggilan tab lain, misalnya:

message_broadcast({'command':'reset'})

Ingatlah bahwa mengirim pesan yang sama persis dua kali akan diperbanyak hanya sekali, jadi jika Anda perlu mengulangi pesan, tambahkan beberapa pengidentifikasi unik kepadanya, seperti

message_broadcast({'command':'reset', 'uid': (new Date).getTime()+Math.random()})

Juga ingat bahwa tab saat ini yang menyiarkan pesan tidak benar-benar menerimanya, hanya tab atau jendela lain di domain yang sama.

Anda dapat bertanya apa yang terjadi jika pengguna memuat halaman web yang berbeda atau menutup tabnya tepat setelah panggilan setItem () sebelum removeItem (). Nah, dari pengujian saya sendiri browser menahan bongkar sampai seluruh fungsi message_broadcast()selesai. Saya diuji untuk menaruh beberapa siklus yang sangat lama untuk () dan masih menunggu sampai siklus selesai sebelum ditutup. Jika pengguna membunuh tab hanya di antara, maka browser tidak akan punya cukup waktu untuk menyimpan pesan ke disk, sehingga pendekatan ini bagi saya seperti cara aman mengirim pesan tanpa jejak. Komentar diterima.

Tomas M
sumber
1
dapatkah Anda mengabaikan acara hapus sebelum memohon JSON.parse ()?
Dandavis
1
ingat batas data acara, termasuk data penyimpanan lokal yang sudah ada sebelumnya. mungkin lebih baik / aman untuk menggunakan acara penyimpanan hanya untuk pengiriman pesan daripada pengiriman. seperti ketika Anda mendapatkan kartu pos yang memberitahu Anda untuk mengambil paket di kantor pos ... juga, penyimpanan lokal pergi ke hard drive, sehingga mungkin meninggalkan cache yang tidak disengaja dan mempengaruhi log, yang merupakan alasan lain untuk mempertimbangkan mekanisme transportasi yang berbeda untuk data aktual.
Dandavis
1
saya melakukan sesuatu yang berhubungan beberapa waktu lalu: danml.com/js/localstorageevents.js , bahwa seseorang memiliki basis emmiter acara dan "gema lokal" sehingga Anda dapat menggunakan EE untuk semuanya di mana saja.
Dandavis
7
Safari tidak mendukung BroadcastChannel - caniuse.com/#feat=broadcastchannel
Srikanth
1
Hanya dengan kepala, Anda juga dapat menggunakan pekerja bersama: developer.mozilla.org/en-US/docs/Web/API/SharedWorker (yang memiliki dukungan lebih baik di seluruh browser)
Seblor
116

Ada API modern yang didedikasikan untuk tujuan ini - Saluran Siaran

Semudah:

var bc = new BroadcastChannel('test_channel');

bc.postMessage('This is a test message.'); /* send */

bc.onmessage = function (ev) { console.log(ev); } /* receive */

Tidak perlu pesan hanya menjadi DOMString, segala jenis objek dapat dikirim.

Mungkin, terlepas dari kebersihan API, ini adalah manfaat utama API ini - tidak ada pengerasan objek.

Saat ini hanya didukung di Chrome dan Firefox, tetapi Anda dapat menemukan polyfill yang menggunakan localStorage.

pengguna
sumber
3
Tunggu, bagaimana Anda tahu dari mana pesan itu berasal? Apakah ini mengabaikan pesan yang berasal dari tab yang sama?
AturSams
2
@zehelvion: Pengirim tidak akan menerimanya, sesuai dengan contoh ikhtisar yang bagus ini . Selain itu, Anda dapat memasukkan pesan apa pun yang Anda inginkan, termasuk. beberapa ID pengirim, jika perlu.
Sz.
7
Ada proyek bagus yang merangkum
james2doyle
Apakah ada sinyal publik dari Safari tentang apakah dukungan untuk API ini akan masuk atau tidak di browser itu?
Casey
@AturSams Anda memeriksa apakah MessageEvent.origin, MessageEvent.source atau MessageEvent.ports adalah yang Anda inginkan. Seperti biasa, dokumentasi adalah tempat terbaik untuk memulai: developer.mozilla.org/en-US/docs/Web/API/MessageEvent
Stefan Mihai Stanescu
40

Bagi mereka yang mencari solusi yang tidak berdasarkan jQuery, ini adalah versi JavaScript sederhana dari solusi yang disediakan oleh Thomas M:

window.addEventListener("storage", message_receive);

function message_broadcast(message) {
    localStorage.setItem('message',JSON.stringify(message));
}

function message_receive(ev) {
    if (ev.key == 'message') {
        var message=JSON.parse(ev.newValue);
    }
}
Nacho Coloma
sumber
1
Mengapa Anda menghilangkan panggilan removeItem?
Tomas M
2
Saya hanya fokus pada perbedaan antara jQuery dan JavaScript.
Nacho Coloma
Saya selalu menggunakan lib karena polyfill dan kemungkinan fitur yang tidak didukung!
Amin Rahimi
20

Checkout AcrossTabs - Komunikasi yang mudah antara tab browser lintas asal. Itu menggunakan kombinasi postMessage dan sessionStorage API untuk membuat komunikasi lebih mudah dan dapat diandalkan.


Ada pendekatan yang berbeda dan masing-masing memiliki kelebihan dan kekurangan. Mari kita bahas masing-masing:

  1. Penyimpanan lokal

    Pro :

    1. Penyimpanan web dapat dilihat secara sederhana sebagai peningkatan cookie, memberikan kapasitas penyimpanan yang jauh lebih besar. Jika Anda melihat kode sumber Mozilla, kita dapat melihat bahwa 5120KB ( 5MB yang sama dengan 2,5 Juta karakter pada Chrome) adalah ukuran penyimpanan default untuk seluruh domain. Ini memberi Anda lebih banyak ruang untuk bekerja daripada cookie 4KB pada umumnya.
    2. Data tidak dikirim kembali ke server untuk setiap permintaan HTTP (HTML, gambar, JavaScript, CSS, dll) - mengurangi jumlah lalu lintas antara klien dan server.
    3. Data yang disimpan di localStorage bertahan hingga dihapus secara eksplisit. Perubahan yang dibuat disimpan dan tersedia untuk semua kunjungan saat ini dan di masa mendatang ke situs.

    Cons :

    1. Ini bekerja pada kebijakan yang sama asal . Jadi, data yang disimpan hanya akan tersedia pada sumber yang sama.
  2. Kue

    Pro:

    1. Dibandingkan dengan yang lain, tidak ada yang AFAIK.

    Cons:

    1. Batas 4K adalah untuk seluruh cookie, termasuk nama, nilai, tanggal kedaluwarsa, dll. Untuk mendukung sebagian besar browser, jaga nama di bawah 4000 byte, dan ukuran cookie keseluruhan di bawah 4093 byte.
    2. Data dikirim kembali ke server untuk setiap permintaan HTTP (HTML, gambar, JavaScript, CSS, dll) - meningkatkan jumlah lalu lintas antara klien dan server.

      Biasanya, yang berikut ini diizinkan:

      • Total 300 cookie
      • 4096 byte per cookie
      • 20 cookie per domain
      • 81920 byte per domain (Diberikan 20 cookie dengan ukuran maksimal 4096 = 81920 byte.)
  3. sessionStorage

    Pro:

    1. Ini mirip dengan localStorage.
    2. Perubahan hanya tersedia per jendela (atau tab di browser seperti Chrome dan Firefox). Perubahan yang dibuat disimpan dan tersedia untuk halaman saat ini, serta kunjungan mendatang ke situs pada jendela yang sama. Setelah jendela ditutup, penyimpanan dihapus

    Cons:

    1. Data hanya tersedia di dalam jendela / tab di mana itu diatur.
    2. Data tidak persisten yaitu akan hilang setelah jendela / tab ditutup.
    3. Seperti localStorage, ini bekerja pada kebijakan asal-sama . Jadi, data yang disimpan hanya akan tersedia pada sumber yang sama.
  4. PostMessage

    Pro:

    1. Aman memungkinkan komunikasi lintas asal .
    2. Sebagai titik data, implementasi WebKit (digunakan oleh Safari dan Chrome) saat ini tidak memberlakukan batasan apa pun (selain yang dikenakan oleh kehabisan memori).

    Cons:

    1. Perlu membuka jendela dari jendela saat ini dan kemudian dapat berkomunikasi hanya selama Anda tetap membuka jendela.
    2. Masalah keamanan - Mengirim string melalui postMessage adalah Anda akan mengambil peristiwa postMessage lain yang diterbitkan oleh plugin JavaScript lainnya, jadi pastikan untuk menerapkantargetOrigindan memeriksa kewarasan untuk data yang diteruskan ke pendengar pesan.
  5. Kombinasi dari PostMessage + SessionStorage

    Menggunakan postMessage untuk berkomunikasi antara banyak tab dan sekaligus menggunakan sessionStorage di semua tab / jendela yang baru dibuka untuk mempertahankan data yang sedang lewat. Data akan tetap ada selama tab / windows tetap dibuka. Jadi, bahkan jika tab / jendela pembuka ditutup, tab / jendela yang dibuka akan memiliki seluruh data bahkan setelah disegarkan.

Saya telah menulis pustaka JavaScript untuk ini, bernama AcrossTabs yang menggunakan postMessage API untuk berkomunikasi antara tab asal / jendela dan sessionStorage untuk tetap membuka tab / identitas jendela yang dibuka selama masih hidup.

softvar
sumber
Dengan menggunakan AcrossTabs, apakah mungkin untuk membuka situs web yang berbeda di tab lain, dan mendapatkan data dari itu ke tab induk? Saya akan memiliki detail otentikasi untuk situs web lain.
Madhur Bhaiya
1
Ya, Anda dapat @MadhurBhaiya
softvar
Keuntungan terbesar dari cookie adalah memungkinkan cross origin domain yang sama, yang biasanya berguna ketika Anda memiliki seperangkat asal-usul seperti "a.target.com", "b.target.com" dll.
StarPinkER
7

Metode lain yang harus dipertimbangkan orang untuk digunakan adalah Pekerja Bersama. Saya tahu ini adalah konsep yang mutakhir, tetapi Anda dapat membuat relay pada Pekerja Bersama yang JAUH lebih cepat daripada penyimpanan lokal, dan tidak memerlukan hubungan antara jendela induk / anak, asalkan Anda menggunakan asal yang sama.

Lihat jawaban saya di sini untuk beberapa diskusi yang saya buat tentang ini.

datasedai
sumber
7

Ada komponen open-source kecil untuk menyinkronkan / berkomunikasi antara tab / windows dengan asal yang sama (penafian - saya salah satu kontributor!) Yang berbasis di sekitar localStorage.

TabUtils.BroadcastMessageToAllTabs("eventName", eventDataString);

TabUtils.OnBroadcastMessage("eventName", function (eventDataString) {
    DoSomething();
});

TabUtils.CallOnce("lockname", function () {
    alert("I run only once across multiple tabs");
});

https://github.com/jitbit/TabUtils

PS Saya mengambil kebebasan untuk merekomendasikannya di sini karena sebagian besar komponen "kunci / mutex / sinkronisasi" gagal pada koneksi websocket ketika peristiwa terjadi hampir secara bersamaan

Alex
sumber
6

Saya telah membuat perpustakaan sysend.js , ini sangat kecil, Anda dapat memeriksa kode sumbernya. Perpustakaan tidak memiliki dependensi eksternal.

Anda dapat menggunakannya untuk komunikasi antara tab / windows di browser dan domain yang sama. Perpustakaan menggunakan BroadcastChannel, jika didukung, atau acara penyimpanan dari localStorage.

API sangat sederhana:

sysend.on('foo', function(message) {
    console.log(message);
});
sysend.broadcast('foo', {message: 'Hello'});
sysend.broadcast('foo', "hello");
sysend.broadcast('foo'); // empty notification

ketika browser Anda mendukung BroadcastChannel, ia mengirim objek literal (tetapi sebenarnya serial-otomatis oleh browser) dan jika tidak serial ke JSON pertama dan deserialized di ujung lainnya.

Versi terbaru juga memiliki helper API untuk membuat proxy untuk komunikasi Cross-Domain. (memerlukan file html tunggal pada domain target).

Ini demo .

EDIT :

Versi baru juga mendukung komunikasi Lintas-Domain , jika Anda menyertakan proxy.htmlfile khusus pada domain target dan proxyfungsi panggilan dari domain sumber:

sysend.proxy('https://target.com');

(proxy.html ini adalah file html yang sangat sederhana, yang hanya memiliki satu tag skrip dengan perpustakaan).

Jika Anda ingin komunikasi dua arah, Anda harus melakukan hal yang sama pada target.comdomain.

CATATAN : Jika Anda akan menerapkan fungsionalitas yang sama menggunakan localStorage, ada masalah di IE. Acara penyimpanan dikirim ke jendela yang sama, yang memicu acara dan untuk browser lain hanya dipanggil untuk tab / jendela lain.

jcubic
sumber
2
Hanya ingin memberi Anda beberapa pujian untuk ini. Tambahan manis yang bagus dan sederhana yang memungkinkan saya berkomunikasi di antara tab saya untuk menjaga agar perangkat lunak logout tidak menendang orang. Pekerjaan yang baik. Saya sangat merekomendasikan ini jika Anda ingin solusi pengiriman pesan yang mudah digunakan.
BrownPony
4

Saya membuat modul yang bekerja sama dengan Broadcastchannel resmi tetapi memiliki cadangan berdasarkan penyimpanan lokal, indeks dan unix-soket. Ini memastikan itu selalu berfungsi bahkan dengan Webworkers atau NodeJS. Lihat pubkey: BroadcastChannel

kemaluan
sumber
1

Saya menulis artikel tentang ini di blog saya: http://www.ebenmonney.com/blog/how-to-implement-remember-me-functionality-using-token-based-authentication-and-localstorage-in-a- aplikasi web .

Menggunakan perpustakaan yang saya buat storageManagerAnda dapat mencapai ini sebagai berikut:

storageManager.savePermanentData('data', 'key'): //saves permanent data
storageManager.saveSyncedSessionData('data', 'key'); //saves session data to all opened tabs
storageManager.saveSessionData('data', 'key'); //saves session data to current tab only
storageManager.getData('key'); //retrieves data

Ada metode nyaman lainnya juga untuk menangani skenario lain juga

adentum
sumber
0

Ini adalah storagebagian pengembangan dari jawaban Tomas M untuk Chrome. Kita harus menambahkan pendengar

window.addEventListener("storage", (e)=> { console.log(e) } );

Muat / simpan item dalam penyimpanan tidak meruntuhkan acara ini - kami HARUS memicu secara manual oleh

window.dispatchEvent( new Event('storage') ); // THIS IS IMPORTANT ON CHROME

dan sekarang, semua tab terbuka akan menerima acara

Kamil Kiełczewski
sumber