javascript - que - Comunicación entre pestañas o ventanas
que es gmail (9)
Estaba buscando una manera de comunicarme entre múltiples pestañas o ventanas en un navegador (en el mismo dominio, no CORS) sin dejar rastros. Hubo varias soluciones:
La primera es probablemente la peor solución: debe abrir una ventana desde su ventana actual y luego puede comunicarse solo mientras mantenga las ventanas abiertas. Si vuelve a cargar la página en cualquiera de las ventanas, lo más probable es que haya perdido la comunicación.
El segundo enfoque, usando postMessage, probablemente habilita la comunicación de origen cruzado, pero sufre el mismo problema que el primer enfoque. Necesita mantener un objeto de ventana.
La tercera forma, utilizando cookies, almacena algunos datos en el navegador, lo que efectivamente puede parecer enviar un mensaje a todas las ventanas en el mismo dominio, pero el problema es que nunca se puede saber si todas las pestañas leen el "mensaje" o no antes. limpiar. Debe implementar algún tipo de tiempo de espera para leer la cookie periódicamente. Además, está limitado por la longitud máxima de la cookie, que es 4KB.
La cuarta solución, usando localStorage, parecía superar las limitaciones de las cookies, e incluso se puede escuchar usando eventos. Cómo usarlo se describe en la respuesta aceptada.
Editar 2018: la respuesta aceptada aún funciona, pero hay una solución más nueva para los navegadores modernos, usar BroadcastChannel. Consulte la otra respuesta para ver un ejemplo simple que describe cómo transmitir mensajes fácilmente entre pestañas utilizando BroadcastChannel.
Checkout AcrossTabs : comunicación fácil entre pestañas de navegador de origen cruzado. Utiliza una combinación de API postMessage y sessionStorage para hacer que la comunicación sea mucho más fácil y confiable.
Existen diferentes enfoques y cada uno tiene sus propias ventajas y desventajas. Vamos a discutir cada uno:
-
Pros :
- El almacenamiento web puede verse de manera simple como una mejora en las cookies, proporcionando una capacidad de almacenamiento mucho mayor. Si observa el código fuente de Mozilla, podemos ver que 5120 KB ( 5 MB, que equivale a 2.5 millones de caracteres en Chrome) es el tamaño de almacenamiento predeterminado para un dominio completo. Esto le brinda considerablemente más espacio para trabajar que una cookie típica de 4KB.
- Los datos no se envían de vuelta al servidor para cada solicitud HTTP (HTML, imágenes, JavaScript, CSS, etc.), lo que reduce la cantidad de tráfico entre el cliente y el servidor.
- Los datos almacenados en localStorage persisten hasta que se eliminen explícitamente. Los cambios realizados se guardan y están disponibles para todas las visitas actuales y futuras al sitio.
Contras :
- Funciona en la política del mismo origen . Por lo tanto, los datos almacenados solo estarán disponibles en el mismo origen.
-
Pros:
- En comparación con otros, no hay nada AFAIK.
Contras:
- El límite de 4K es para toda la cookie, incluido el nombre, el valor, la fecha de caducidad, etc. Para admitir la mayoría de los navegadores, mantenga el nombre por debajo de 4000 bytes y el tamaño total de la cookie por debajo de 4093 bytes.
-
Los datos se envían de vuelta al servidor para cada solicitud HTTP (HTML, imágenes, JavaScript, CSS, etc.), lo que aumenta la cantidad de tráfico entre el cliente y el servidor.
Por lo general, se permiten los siguientes:
- 300 galletas en total
- 4096 bytes por cookie
- 20 cookies por dominio
- 81920 bytes por dominio (Dadas 20 cookies de tamaño máximo 4096 = 81920 bytes).
-
Pros:
-
Es similar a
localStorage
. - Los cambios solo están disponibles por ventana (o pestaña en navegadores como Chrome y Firefox). Los cambios realizados se guardan y están disponibles para la página actual, así como para futuras visitas al sitio en la misma ventana. Una vez que se cierra la ventana, se elimina el almacenamiento
Contras:
- Los datos solo están disponibles dentro de la ventana / pestaña en la que se configuraron.
- Los datos no son persistentes, es decir, se perderán una vez que se cierre la ventana / pestaña.
-
Al igual que
localStorage
, tt funciona en la política del mismo origen . Por lo tanto, los datos almacenados solo estarán disponibles en el mismo origen.
-
Es similar a
-
Pros:
- Con seguridad permite la comunicación de cross-origin .
- Como punto de datos, la implementación de WebKit (utilizada por Safari y Chrome) actualmente no impone ningún límite (aparte de los impuestos por quedarse sin memoria).
Contras:
- Necesita abrir una ventana desde la ventana actual y luego puede comunicarse solo mientras mantenga las ventanas abiertas.
-
Problemas de
seguridad
: el envío de cadenas a través de postMessage es que recogerá otros eventos postMessage publicados por otros complementos de JavaScript, así que asegúrese de implementar un
targetOrigin
y una comprobación detargetOrigin
de los datos que se transmiten al escucha de mensajes.
-
Una combinación de postMessage + sessionStorage
Usando postMessage para comunicarse entre múltiples pestañas y al mismo tiempo usando sessionStorage en todas las pestañas / ventanas recién abiertas para persistir la transmisión de datos. Los datos se conservarán mientras las pestañas / ventanas permanezcan abiertas. Por lo tanto, incluso si la pestaña / ventana del abridor se cierra, las pestañas / ventanas abiertas tendrán todos los datos incluso después de actualizarse.
He escrito una biblioteca de JavaScript para esto, llamada AcrossTabs que usa la API postMessage para comunicarse entre pestañas / ventanas de origen cruzado y sessionStorage para mantener la identidad de pestañas / ventanas abiertas mientras vivan.
Creé un módulo que funciona igual que el github.com/pubkey/broadcast-channel oficial pero tiene fallos basados en localstorage, indexeddb y unix-sockets. Esto asegura que siempre funcione incluso con Webworkers o NodeJS. Ver github.com/pubkey/broadcast-channel
Escribí un artículo sobre esto en mi blog: ebenmonney.com/blog/…
Usando una biblioteca que creé
storageManager
puede lograr esto de la siguiente manera:
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
También hay otros métodos convenientes para manejar otros escenarios.
Hay un pequeño componente de código abierto para sincronizar / comunicarse entre pestañas / ventanas del mismo origen (descargo de responsabilidad: ¡soy uno de los contribuyentes!) Basado en
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
PD: Me tomé la libertad de recomendarlo aquí, ya que la mayoría de los componentes "lock / mutex / sync" fallan en las conexiones websocket cuando los eventos ocurren casi simultáneamente
Hay una API moderna dedicada para este propósito: Broadcast Channel
Es tan fácil como:
var bc = new BroadcastChannel(''test_channel'');
bc.postMessage(''This is a test message.''); /* send */
bc.onmessage = function (ev) { console.log(ev); } /* receive */
No es necesario que el mensaje sea solo un DOMString, se puede enviar cualquier tipo de objeto.
Probablemente, aparte de la limpieza de la API, es el principal beneficio de esta API: no hay cadena de objetos.
Actualmente solo se caniuse.com/#feat=broadcastchannel en Chrome y Firefox, pero puede encontrar un polyfill que use localStorage.
He creado una biblioteca sysend.js , es muy pequeña, puedes consultar su código fuente. La biblioteca no tiene dependencias externas.
Puede usarlo para la comunicación entre pestañas / ventanas en el mismo navegador y dominio. La biblioteca usa BroadcastChannel, si es compatible, o un evento de almacenamiento de localStorage.
API es muy simple:
sysend.on(''foo'', function(message) {
console.log(message);
});
sysend.broadcast(''foo'', {message: ''Hello''});
sysend.broadcast(''foo'', "hello");
sysend.broadcast(''foo''); // empty notification
cuando su navegador admite BroadcastChannel, envía un objeto literal (pero de hecho es auto serializado por el navegador) y si no es serializado primero a JSON y deserializado en el otro extremo.
La versión reciente también tiene API auxiliar para crear proxy para la comunicación entre dominios. (requiere un único archivo html en el dominio de destino).
Aquí está la jcubic.pl/sysend.php .
EDITAR :
La nueva versión también admite la comunicación entre
dominios
, si incluye un archivo
proxy.html
especial en el dominio de destino y llama a la función de
proxy
desde el dominio de origen:
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
(proxy.html es un archivo html muy simple, que solo tiene una etiqueta de script con la biblioteca).
Si desea una comunicación bidireccional, debe hacer lo mismo en el dominio
target.com
.
NOTA : Si implementará la misma funcionalidad usando localStorage, hay un problema en IE. El evento de almacenamiento se envía a la misma ventana, lo que activó el evento y para otros navegadores solo se invoca para otras pestañas / ventanas.
Otro método que la gente debería considerar usar es Trabajadores Compartidos. Sé que es un concepto de vanguardia, pero puede crear un relé en un Trabajador compartido que sea MUCHO más rápido que el almacenamiento local y que no requiera una relación entre la ventana padre / hijo, siempre y cuando esté en el mismo origen.
Vea mi respuesta here para una discusión que hice sobre esto.
Para aquellos que buscan una solución no basada en jQuery, esta es una versión simple de JavaScript de la solución provista por 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);
}
}
Editar 2018: es mejor que use BroadcastChannel para este propósito, vea otras respuestas a continuación. Sin embargo, si aún prefiere usar el almacenamiento local para la comunicación entre pestañas, hágalo de esta manera:
Para recibir una notificación cuando una pestaña envía un mensaje a otras pestañas, simplemente necesita vincular el evento ''almacenamiento''. En todas las pestañas, haz esto:
$(window).on(''storage'', message_receive);
Se llamará a la función
message_receive
cada vez que establezca cualquier valor de localStorage en cualquier otra pestaña.
El detector de eventos también contiene los datos recién configurados en localStorage, por lo que ni siquiera necesita analizar el objeto localStorage en sí.
Esto es muy útil porque puede restablecer el valor justo después de que se estableció, para limpiar eficazmente cualquier rastro.
Aquí hay funciones para la mensajería:
// 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.
}
Entonces, una vez que sus pestañas se unen en el evento de almacenamiento y tiene implementadas estas dos funciones, simplemente puede transmitir un mensaje a otras pestañas llamando, por ejemplo:
message_broadcast({''command'':''reset''})
Recuerde que enviar el mismo mensaje exactamente dos veces se propagará solo una vez, por lo que si necesita repetir mensajes, agrégueles un identificador único, como
message_broadcast({''command'':''reset'', ''uid'': (new Date).getTime()+Math.random()})
Recuerde también que la pestaña actual que transmite el mensaje en realidad no lo recibe, solo otras pestañas o ventanas en el mismo dominio.
Puede preguntar qué sucede si el usuario carga una página web diferente o cierra su pestaña justo después de la llamada setItem () antes de removeItem ().
Bueno, según mis propias pruebas, el navegador pone la descarga en espera hasta que la función completa
message_broadcast()
haya finalizado.
Probé para poner dentro un ciclo muy largo para () y todavía esperó a que el ciclo terminara antes de cerrar.
Si el usuario mata la pestaña justo en el medio, entonces el navegador no tendrá tiempo suficiente para guardar el mensaje en el disco, por lo que este enfoque me parece una forma segura de cómo enviar mensajes sin ningún rastro.
Comentarios bienvenidos