De vez en cuando, los desarrolladores necesitan establecer comunicación entre varias pestañas del navegador para poder enviar mensajes de una pestaña a otra y recibir respuestas. También nos hemos enfrentado a esta necesidad en algún momento.
Algunas soluciones ya existen (como, por ejemplo, BroadcastChannel API). Sin embargo, el soporte de su navegador deja mucho que desear , así que decidimos usar nuestra propia biblioteca. Cuando la biblioteca estaba lista, esa funcionalidad ya no era necesaria. Sin embargo, surgió otra tarea: la comunicación entre un iframe y la ventana principal.
En un examen más detallado, resultó que dos tercios de la biblioteca no tendrían que ser cambiados, solo era necesaria una refactorización de código. La biblioteca es un PROTOCOLO de comunicación que puede trabajar con datos de texto. Se puede aplicar en todos los casos en los que se transfiere texto, como iframes, window.open, trabajador, pestañas del navegador o WebSocket.
Como funciona
Actualmente, el protocolo tiene dos funciones: enviar mensajes y suscripción a eventos. Cualquier mensaje en el protocolo es un objeto de datos. Para nosotros, el campo principal en ese objeto es el tipo , que nos dice qué tipo de mensaje es. El campo de tipo es una enumeración con los siguientes valores:
- 0 - enviando un mensaje
- 1 - enviando una solicitud
- 2 - recibiendo una respuesta.
Enviando un mensaje
Enviar un mensaje no implica una respuesta. Para enviar un mensaje, creamos un objeto con los siguientes campos:
- tipo - tipo de evento 0
- nombre : nombre del evento del usuario
- datos : datos de usuario (tipo JSON).
Al recibir un mensaje en el otro lado con el campo de tipo = 0 , sabemos que es un evento, con un nombre de evento existente y datos. Todo lo que tenemos que hacer es transmitir el evento (casi un patrón EventEmitter estándar).
Cómo funciona en un esquema simple:

Enviando una solicitud
Enviar una solicitud implica que dentro de la biblioteca, se crea una identificación de solicitud y la biblioteca esperará una respuesta con la identificación . Al recibir con éxito una respuesta, se eliminarán todos los campos auxiliares y la respuesta se devolverá al usuario. Además, se puede establecer el tiempo de respuesta máximo.

En cuanto a las solicitudes, esto es un poco más complicado. Para responder a una solicitud, debe anunciar los métodos disponibles en nuestro protocolo. Esto se hace con el método registerRequestHandler . Acepta el nombre de una solicitud de respuesta y una función que devuelve la respuesta. Para crear una solicitud, necesitamos una identificación , y básicamente podemos usar la marca de tiempo , pero no es muy conveniente ajustarla. Entonces, esta es una ID de clase que envía una respuesta + número de respuesta + literal de cadena. Ahora creamos un objeto con los siguientes campos: id , tipo = 1 , nombre como nombre de solicitud y datos como datos de usuario (como JSON).
Al recibir una solicitud, verificamos si tenemos una API para responder a esta solicitud, y si no la tenemos, devolvemos un mensaje de error. Si tenemos una API, devolvemos el resultado de ejecutar la función desde registerRequestHandler , con el nombre de la solicitud correspondiente.
Para la respuesta, creamos un objeto con los campos: tipo = 2, id como el ID del mensaje al que respondemos, estado como un campo que dice si esta respuesta es un error (si no tenemos una API, o el controlador incurrió en un error, o el usuario devolvió una promesa rechazada, u ocurre otro error (serializar)), y el contenido como datos de respuesta.
Entonces, hemos descrito el funcionamiento del protocolo, que ejecuta la clase Bus pero no ha explicado el proceso de envío y recepción de mensajes. Para eso, necesitamos adaptadores de clase con tres métodos.
- enviar es un método básicamente responsable de enviar mensajes
- addListener es un método para suscribirse a eventos
- destroy es un método para eliminar suscripciones al eliminar Bus .
Adaptadores. Ejecución del protocolo.
Para iniciar el protocolo, actualmente, solo el adaptador para trabajar con iframe / window está listo. Utiliza postMessage y addEventListener . Es bastante sencillo: debe enviar un mensaje a postMessage con un origen correcto y escuchar mensajes a través de addEventListener en el evento "mensaje" .
Encontramos algunos matices:
- Siempre debe escuchar las respuestas en SU ventana y enviarlas en la OTRA ventana (iframe, abridor, padre, trabajador, etc.). Si intenta escuchar un mensaje en la ventana OTRO y el origen difiere del actual, se producirá un error.
- Al recibir un mensaje, asegúrese de que esté dirigido a usted: la ventana puede acomodar muchos mensajes analíticos, WebStorm (si lo usa) y otros iframes, por lo que debe asegurarse de que el evento esté en su protocolo y esté destinado a usted.
- No puede devolver una promesa con una copia de Windows , porque la promesa al devolver el resultado, intentará verificar si el resultado tiene el método de entonces . Si no tiene acceso a la ventana (por ejemplo, una ventana con otro origen), se producirá un error (aunque no en todos los navegadores). Para evitar este problema, sería suficiente envolver la ventana en el objeto y poner un objeto en la promesa que tiene un enlace a la ventana correcta.
Ejemplos de uso:
La biblioteca está disponible en NPM y puede instalarla fácilmente a través de su administrador de paquetes: @ waves / waves-browser-bus
Para establecer una comunicación bidireccional con un iframe, es suficiente usar este código:
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 interior:
import { Bus, WindowAdapter } from '@waves/waves-browser-bus'; WindowAdapter.createSimpleWindowAdapter().then(adapter => { const bus = new Bus(adapter); bus.dispatchEvent('ready', null);
Que sigue
Tenemos un protocolo flexible y versátil que puede usarse en cualquier situación. A continuación, planeo separar los adaptadores del protocolo y ponerlos en paquetes npm separados, y agregar adaptadores para las pestañas de trabajador y navegador. Quiero escribir adaptadores que ejecuten el protocolo para cualquier otro propósito para que sea lo más fácil posible. Si desea unirse al proceso de desarrollo o tener ideas sobre la funcionalidad de la biblioteca, puede ponerse en contacto en el repositorio .