Comunicazione tra tabs o windows

Stavo cercando un modo per comunicare tra più tabs o windows in un browser (sullo stesso dominio, non CORS) senza lasciare tracce. C’erano diverse soluzioni:

  1. usando l’object finestra
  2. postMessage
  3. biscotti
  4. memoria locale

La prima è probabilmente la soluzione peggiore: è necessario aprire una finestra dalla finestra corrente e quindi è ansible comunicare solo fino a quando si mantiene aperta la finestra. Se ricarichi la pagina in una delle windows, molto probabilmente hai perso la comunicazione.

Il secondo approccio, utilizzando postMessage, probabilmente abilita la comunicazione tra origini, ma presenta lo stesso problema del primo approccio. È necessario mantenere un object finestra.

Terzo modo, utilizzando i cookie, memorizza alcuni dati nel browser, che può effettivamente sembrare inviare un messaggio a tutte le windows sullo stesso dominio, ma il problema è che non si può mai sapere se tutte le tabs leggono già il “messaggio” o meno pulire. Devi implementare una sorta di timeout per leggere periodicamente il cookie. Inoltre sei limitato dalla lunghezza massima del cookie, che è 4KB.

La quarta soluzione, utilizzando localStorage, sembrava superare i limiti dei cookie e può persino essere ascoltata utilizzando eventi. Come usarlo è descritto nella risposta accettata.

Modifica 2018: la risposta accettata funziona ancora, ma esiste una nuova soluzione per i browser moderni, per utilizzare BroadcastChannel. Vedere l’altra risposta per un semplice esempio che descrive come trasmettere facilmente messaggi tra le tabs utilizzando BroadcastChannel.

Modifica 2018: è meglio utilizzare BroadcastChannel per questo scopo, vedere altre risposte di seguito. Tuttavia, se si preferisce ancora utilizzare lo spazio locale per la comunicazione tra le tabs, farlo in questo modo:

Per ricevere una notifica quando una scheda invia un messaggio ad altre tabs, è sufficiente collegarsi all’evento ‘memoria’. In tutte le tabs, fai questo:

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

La funzione message_receive verrà chiamata ogni volta che si imposta qualsiasi valore di localStorage in qualsiasi altra scheda. Il listener di eventi contiene anche i dati recentemente impostati su localStorage, quindi non è nemmeno necessario analizzare l’object localStorage stesso. Questo è molto utile perché puoi ripristinare il valore subito dopo averlo impostato, per ripulire efficacemente ogni traccia. Ecco le funzioni per la messaggistica:

 // 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. } 

Quindi ora una volta che le tue tabs si collegano all’evento onstage e hai implementato queste due funzioni, puoi semplicemente trasmettere un messaggio ad altre tabs che chiamano, ad esempio:

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

Ricorda che l’invio dello stesso identico messaggio due volte verrà propagato solo una volta, quindi se hai bisogno di ripetere i messaggi, aggiungi loro un identificatore univoco, come

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

Ricorda inoltre che la scheda corrente che trasmette il messaggio in realtà non la riceve, solo altre tabs o windows nello stesso dominio.

Puoi chiedere cosa succede se l’utente carica una pagina Web diversa o chiude la sua scheda subito dopo la chiamata setItem () prima di removeItem (). Bene, dal mio test il browser mette in attesa lo scaricamento fino a quando l’intera funzione message_broadcast() è finita. Ho provato a mettere un ciclo molto lungo per () e aspettava ancora che il ciclo finisse prima di chiudere. Se l’utente uccide la scheda solo tra parentesi, il browser non avrà abbastanza tempo per salvare il messaggio su disco, quindi questo approccio mi sembra un modo sicuro per inviare messaggi senza lasciare tracce. Commenti benvenuto

C’è una moderna API dedicata a questo scopo – Broadcast Channel

È facile come:

 var bc = new BroadcastChannel('test_channel'); bc.postMessage('This is a test message.'); /* send */ bc.onmessage = function (ev) { console.log(ev); } /* receive */ 

Non è necessario che il messaggio sia solo un DOMString, qualsiasi tipo di object può essere inviato.

Probabilmente, a parte la pulizia dell’API, è il vantaggio principale di questa API: nessuna stringa di oggetti.

Attualmente supportato solo in Chrome e Firefox, ma puoi trovare un polyfill che utilizza localStorage.

Per coloro che cercano una soluzione non basata su jQuery, questa è una semplice versione JavaScript della soluzione fornita da 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); } } 

Checkout AcrossTabsFacile comunicazione tra le tabs del browser di origine incrociata. Utilizza una combinazione di postMessage e sessionStorage API per rendere la comunicazione molto più semplice e affidabile.


Esistono diversi approcci e ognuno ha i suoi vantaggi e svantaggi. Discutiamo ciascuno:

  1. Memoria locale

    Pro :

    1. Lo storage Web può essere visualizzato in modo semplicistico come un miglioramento dei cookie, fornendo una capacità di archiviazione molto maggiore. Se osservi il codice sorgente di Mozilla, possiamo vedere che 5120 KB ( 5 MB equivalenti a 2,5 milioni di caratteri su Chrome) sono le dimensioni di archiviazione predefinite per un intero dominio. Questo ti dà molto più spazio di lavoro rispetto a un tipico cookie 4KB.
    2. I dati non vengono rispediti al server per ogni richiesta HTTP (HTML, immagini, JavaScript, CSS, ecc.) – riducendo la quantità di traffico tra client e server.
    3. I dati memorizzati in localStorage persistono fino all’eliminazione esplicita. Le modifiche apportate vengono salvate e disponibili per tutte le visite attuali e future al sito.

    Contro :

    1. Funziona sulla politica della stessa origine . Quindi, i dati memorizzati saranno disponibili solo sulla stessa origine.
  2. Biscotti

    Professionisti:

    1. Rispetto agli altri, non c’è nulla di AFAIK.

    Contro:

    1. Il limite di 4K è per l’intero cookie, incluso nome, valore, data di scadenza ecc. Per supportare la maggior parte dei browser, mantenere il nome sotto 4000 byte e la dimensione complessiva del cookie sotto 4093 byte.
    2. I dati vengono inviati al server per ogni richiesta HTTP (HTML, immagini, JavaScript, CSS, ecc.) – aumentando la quantità di traffico tra client e server.

      In genere, sono consentiti:

      • 300 biscotti in totale
      • 4096 byte per cookie
      • 20 cookie per dominio
      • 81920 byte per dominio (dati 20 cookie di dimensione massima 4096 = 81920 byte).
  3. sessionStorage

    Professionisti:

    1. È simile a localStorage .
    2. Le modifiche sono disponibili solo per finestra (o scheda in browser come Chrome e Firefox). Le modifiche apportate vengono salvate e disponibili per la pagina corrente, nonché le future visite al sito nella stessa finestra. Una volta chiusa la finestra, la memoria viene cancellata

    Contro:

    1. I dati sono disponibili solo all’interno della finestra / scheda in cui è stato impostato.
    2. I dati non sono persistenti, cioè andranno persi una volta chiusa la finestra / scheda.
    3. Come localStorage , tt funziona sulla politica della stessa origine . Quindi, i dati memorizzati saranno disponibili solo sulla stessa origine.
  4. PostMessage

    Professionisti:

    1. Consente in modo sicuro la comunicazione tra origini .
    2. Come punto dati, l’implementazione WebKit (utilizzata da Safari e Chrome) non applica attualmente alcun limite (tranne quelli imposti dall’esaurimento della memoria).

    Contro:

    1. È necessario aprire una finestra dalla finestra corrente e quindi comunicare solo se si tengono aperte le windows.
    2. Problemi di sicurezza : l’invio di stringhe tramite postMessage implica che raccoglierai altri eventi postMessage pubblicati da altri plugin JavaScript, quindi assicurati di implementare un targetOrigin e un controllo di targetOrigin per i dati trasmessi al listener dei messaggi.
  5. Una combinazione di PostMessage + SessionStorage

    Usando postMessage per comunicare tra più tabs e allo stesso tempo utilizzando sessionStorage in tutte le tabs / windows appena aperte per mantenere i dati passati. I dati verranno mantenuti fino a quando le tabs / windows rimarranno aperte. Quindi, anche se la scheda / finestra di apertura viene chiusa, le tabs / windows aperte avranno l’intero dato anche dopo essere stati aggiornati.

Ho scritto una libreria JavaScript per questo, denominata AcrossTabs che utilizza l’API postMessage per comunicare tra le tabs / windows di origine incrociata e sessionStorage per mantenere l’id quadro aperta di tabs / windows finché vivono.

Un altro metodo che le persone dovrebbero prendere in considerazione è Workers condivisi. So che è un concetto all’avanguardia, ma puoi creare un relay su un lavoratore condiviso che è MOLTO più veloce di localstorage e non richiede una relazione tra la finestra genitore / figlio, purché tu abbia la stessa origine.

Vedi la mia risposta qui per qualche discussione che ho fatto su questo.

C’è un piccolo componente open source per sincronizzare / comunicare tra tabs / windows della stessa origine (dichiarazione di non responsabilità – sono uno dei contributori!) Basato su 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 Mi sono preso la libertà di raccomandarlo qui poiché la maggior parte dei componenti “lock / mutex / sync” falliscono nelle connessioni websocket quando gli eventi accadono quasi simultaneamente

Ho creato un modulo che funziona uguale al Broadcastchannel ufficiale ma ha fallbacks basati su localstorage, indexeddb e unix-socket. Questo assicura che funzioni sempre anche con Webworker o NodeJS. Vedi pubkey: BroadcastChannel

Ho scritto un articolo su questo sul mio blog: http://www.ebenmonney.com/blog/how-to-implement-remember-me-functionality-using-token-based-authentication-and-localstorage-in-a- applicazione web .

Utilizzando una libreria che ho creato storageManager puoi ottenere ciò come segue:

 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 

Esistono anche altri metodi convenienti per gestire anche altri scenari