La API de mensajería de canal

La API de mensajería de canal permite que los iframes y los trabajadores se comuniquen con el hilo del documento principal, pasando mensajes

Introducción a la API de mensajería de canal

Dados dos scripts que se ejecutan en el mismo documento, pero en un contexto diferente, la API de mensajería de canal les permite comunicarse pasando mensajes a través de un canal.

Este caso de uso implica la comunicación entre

  • el documento y un iframe
  • dos iframes
  • dos documentos

Cómo funciona

Vocaciónnew MessageChannel()se inicializa un canal de mensajes.

const channel = new MessageChannel()

El canal tiene 2 propiedades, llamadas

  • puerto1
  • puerto 2

Esas propiedades son un objeto MessagePort.port1es el puerto utilizado por la parte que creó el canal, yport2es el puerto utilizado por el receptor del canal (por cierto, el canal es bidireccional, por lo que el receptor también puede enviar mensajes).

En cada extremo del canal, escucha en un puerto y envía mensajes al otro puerto.

El envío del mensaje se realiza a través del

otherWindow.postMessage()

método, dondeotherWindowes el otro contexto de navegación.

Acepta un mensaje, un origen y el puerto.

Por ejemplo:

const data = { name: 'Flavio' }
const channel = new MessageChannel()
window.postMessage(data, [channel.port2])

Un mensaje puede ser cualquiera de esos valores admitidos:

"Origen" es un URI (p. Ej.https://example.org). Puedes usar'*'para permitir una verificación menos estricta, o especificar un dominio, o especificar'/'para establecer un destino del mismo dominio, sin necesidad de especificar qué dominio es.

El otro contexto de navegación escucha el mensaje usando elmessageevento:

self.addEventListener('message', event => {
  console.log('A new message arrived!')
})

selfes lo mismo que usarwindowen este caso

Dentro del controlador de eventos podemos acceder a los datos enviados mirando eldatapropiedad del objeto de evento:

self.addEventListener('message', event => {
  console.log('A new message arrived!')
  console.log(event.data)
})

Podemos responder usandoMessagePort.postMessage:

self.addEventListener('message', event => {
  console.log('A new message arrived!')
  console.log(event.data)

const data = { someData: ‘hey’ } event.ports[0].postMessage(data) })

Un canal se puede cerrar invocando elclose()método en el puerto :.

self.addEventListener('message', event => {
  console.log('A new message arrived!')
  console.log(event.data)

const data = { someData: ‘hey’ } event.ports[0].postMessage(data) event.ports[0].close() })

Un ejemplo con un iframe

Aquí hay un ejemplo de una comunicación que ocurre entre un documento y uniframeincrustado en él.

El documento principal define uniframey unspandonde imprimiremos un mensaje que se envía desde eliframedocumento. Tan pronto comoiframeel documento está cargado, le enviamos un mensaje en elchannelcreamos.

<!DOCTYPE html>
<html>
  <body>
    <iframe src="iframe.html" width="500" height="500"></iframe>
    <span></span>
  </body>
  <script>
    const channel = new MessageChannel()
    const display = document.querySelector('span')
    const iframe = document.querySelector('iframe')
<span style="color:#a6e22e">iframe</span>.<span style="color:#a6e22e">addEventListener</span>(<span style="color:#e6db74">'load'</span>, () =&gt; {
  <span style="color:#a6e22e">iframe</span>.<span style="color:#a6e22e">contentWindow</span>.<span style="color:#a6e22e">postMessage</span>(<span style="color:#e6db74">'Hey'</span>, <span style="color:#e6db74">'*'</span>, [<span style="color:#a6e22e">channel</span>.<span style="color:#a6e22e">port2</span>])
}, <span style="color:#66d9ef">false</span>)

<span style="color:#a6e22e">channel</span>.<span style="color:#a6e22e">port1</span>.<span style="color:#a6e22e">onmessage</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">event</span> =&gt; {
  <span style="color:#a6e22e">display</span>.<span style="color:#a6e22e">innerHTML</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">event</span>.<span style="color:#a6e22e">data</span>
}

</script> </html>

La fuente de la página iframe es aún más simple:

<!DOCTYPE html>
<html>
  <script>
  window.addEventListener('message', event => {
    // send a message back
    event.ports[0].postMessage('Message back from the iframe')
  }, false)
  </script>
</html>

Como puede ver, ni siquiera necesitamos inicializar un canal, porque elwindow.onmessagehandler se ejecuta automáticamente cuando se recibe el mensaje desde la página del contenedor.

El evento enviado está compuesto por las siguientes propiedades:

  • data: el objeto que se envió desde la otra ventana
  • origin: el URI de origen de la ventana que envió el mensaje
  • source: el objeto de la ventana que envió el mensaje

Siempre verifique el origen del remitente del mensaje.

e.ports[0]es la forma en que hacemos referenciaport2en el iframe, porqueportses una matriz y el puerto se agregó como primer elemento.

Un ejemplo con un trabajador de servicios

Un Service Worker es un trabajador impulsado por eventos, un archivo JavaScript asociado a una página web. Revisar laGuía de trabajadores de serviciospara saber más sobre ellos.

Lo que es importante saber es que los Service Workers están aislados del hilo principal y debemos comunicarnos con ellos mediante mensajes.

Así es como un script adjunto al documento principal manejará el envío de mensajes al Service Worker:

// `worker` is the service worker already instantiated

const messageChannel = new MessageChannel()
messageChannel.port1.addEventListener('message', event => {
  console.log(event.data)
})
worker.postMessage(data, [messageChannel.port2])

En el código de Service Worker, agregamos un detector de eventos para elmessageevento:

self.addEventListener('message', event => {
  console.log(event.data)
})

Y puede devolver mensajes publicando un mensaje amessageChannel.port2, con

self.addEventListener('message', event => {
  event.ports[0].postMessage(data)
})

Soporte de navegador

La API de mensajería de canal es actualmente compatible con todos los navegadores principales, muchos de ellos desde hace mucho tiempo, por lo que incluso las versiones anteriores la admiten. Consulta todos los detalles enhttps://caniuse.com/#feat=channel-messaging.


Más tutoriales de navegador: