2011-12-12 12 views
16

Hasta ahora solo he visto tutoriales para el mensaje posterior en el que una ventana envía un solo tipo de mensaje, y la otra ventana lo interpreta de una sola manera.PostMessage con múltiples funciones o devoluciones de llamada personalizadas

¿Qué pasa si quiero tener muchos tipos diferentes de interacciones entre ventanas, puede postmessage manejar eso?

¿Va contra corriente de lo que se supone que debe hacer la postmensajería?

Por ejemplo, ¿qué ocurre si deseo poder enviar devoluciones de llamada personalizadas, etc.?

+0

No estoy seguro de lo que quiere decir; usted es libre de examinar un mensaje recibido e invocar condicionalmente un código diferente, etc., así que ... – Pointy

+0

Es que hasta ahora, no he visto ningún ejemplo de eso, y por lo que he leído, la correspondencia no puede enviar objetos o cualquier cosa, lo que significa que tendría que diseccionar la cuerda ... simplemente no parece muy limpio. – johnnietheblack

Respuesta

49

Hay un par de maneras de pasar un mensaje de varias partes a un controlador postMessage. La primera (y menos "limpia") es usar un carácter delimitador, luego pasar sus datos a través de una cadena.

Digamos que queríamos pasar una identificación de usuario, una acción y el nombre de los usuarios. La cadena podría tener este aspecto:

54|do_logout|chris

Dentro del manejador postMessage, los datos transmitidos pueden ser split (docs) en el carácter |, a continuación, cada segmento del mensaje puede ser utilizado como sea necesario.

Otra ruta, en lugar de crear/dividir manualmente una cadena, es usar JSON (docs) para convertir un objeto en una cadena en un lado, y usar JSON para convertir de nuevo a un objeto en el manejador.

var pass_data = { 
    'name':'Chris', 
    'id':54, 
    'action':'do_logout' 
}; 
target_window.postMessage(JSON.stringify(pass_data), "http://www.example.net"); 

... entonces en el controlador:

function (event) { 
    var pass_data = JSON.parse(event.data); 
} 

Asegúrese de probar, sin embargo, como el objeto JSON no se proporciona en todas las aplicaciones de usuario, especialmente los más antiguos. Hay muchas (muchas, muchas) bibliotecas de terceros para mejorar el soporte de JSON, así que no dejes que la falta de una adopción completa te asuste: JSON es definitivamente un estándar seguro para "seguir adelante".

¿No sería mejor si pudiéramos pasar ese objeto inmediatamente? Bueno, mirando a Firefox 6 (source), los datos que pases a un manejador de postmensaje pueden ser un objeto. El objeto se serializa, por lo que hay algunas preocupaciones en ese frente, pero:

var pass_data = { 
    'name':'Chris', 
    'id':54, 
    'action':'do_logout' 
}; 
target_window.postMessage(pass_data, "http://www.example.net"); 

Un poco más agradable, ¿eh? Desafortunadamente, las versiones actuales de IE solo se ocuparán de las cadenas. No pude encontrar ninguna discusión sobre planes futuros con respecto a postMessage para IE 10. Además, hay un error conocido en IE 8/9 que rompe postMessage para cualquier cosa que no sean marcos. (source).

Entrar en un aspecto específico de su pregunta - devoluciones de llamada. A menos que pueda pasar la devolución de llamada por nombre de función, no hay forma de pasar una función; no hay funciones anónimas para ti Esto está relacionado con la manera en que los datos se pasan al manejador. En la práctica, no hay soporte para los objetos como datos, detrás de las escenas el navegador está convirtiendo su objeto pasado en una cadena (serialización).

Dicho todo esto, debe comprender que pasar un objeto es exactamente lo mismo que utilizar JSON a stringify un objeto antes de pasar, solo en el caso anterior el navegador está realizando su propia serialización (y posterior deserialización), mientras que con esta última ruta, depende de usted serializar/deserializar.

Los puntos de comida para llevar aquí:

  • postMessage todavía ha limitado cross-browser apoyo
  • La tendencia de las nuevas versiones de los navegadores compatible con los estándares es para permitir el paso de los objetos, además de cadenas
  • El objeto pasado se serializará, por lo que no se permiten referencias de función
  • El soporte más amplio "en la naturaleza" es para datos solo de cadena, lo que significa que tendrá que usar cadenas y "empacar" sus datos como se demostró anteriormente si quieres apoyar una variedad ide de los agentes de usuario
  • Internet Explorer va a arruinar todos los planes que usted hace siempre (incluyendo los días festivos de la familia)

documentación y referencias

+5

¡Gran respuesta! Desearía que hiciera +10 para las vacaciones familiares comentario – johnnietheblack

+1

Siempre puede realizar la codificación y decodificación JSON en cualquier lado del mensaje. – Pointy

+1

Incluso con la serialización JSON, no puede pasar referencias de función (que creo que fue el OP) ya que no se pueden serializar. Este es el caso tanto si JSON codifica el objeto (para IE) o simplemente pasa el objeto (para Firefox> 6). Si intentas pasar una función en Firefox, obtienes el error 'Error: el objeto no pudo ser clonado' :(Lástima, pero quizás esto sea más seguro. –

1

Una forma muy fácil de gatillo devoluciones de llamada sin pasar ningún código real serían:

Objetivo

var callbacks = { 
    myCallback: function() { doSomething(); } 
}; 
window.addEventListener('message', function (ev) { 
    // origin checking etc 
    callbacks[ev.data](); 
}, false); 

Fuente

target.postMessage('myCallback', 'http://www.example.com'); 
3

devoluciones de llamada con postMessage: muy posible y muy útil

hay un buen complemento que he encontrado en npm called "silver-bullet". Hace postMessage con devoluciones de llamada y usa eventEmitter para obtener eventos específicos también. Es muy bonito.

Pero para implementar este me gustaría hacer algo como ...

phostMessage(iframe, someObj, callback); 

que tiene que hacer esto:

  1. Se necesita un ID devolución de llamada común pasado entre bastidores que se comunican.
  2. El remitente crea un único ID de devolución de llamada en cada mensaje y lo almacena en un hash de búsqueda de devolución de llamada para encontrar la devolución de llamada después del envío.
  3. El receptor del mensaje solo asegura que la ID de devolución de llamada se envía de vuelta.
  4. Todos los marcos que se comunican usan la misma biblioteca JS para esto.

Aquí es una demostración muy básica de que:

var callbacks = {}; 

// when receiving messages 
window.addEventListener('message', function(ev) { 
    // todo: add origin check 
    if (!ev.data) 
    return; 

    var message; 
    try { 
    message = JSON.parse(ev.data); 
    } catch (ex) { 
    console.error(ex); 
    } 

    // ignore messages not having a callback ID 
    if (!message || !message.callbackId) 
    return; 

    // we are the sender getting the callback 
    if (callbacks[message.callbackId]) { 
    callbacks[message.callbackId](message); 
    delete callbacks[message.callbackId]; 
    return; 
    } 

    // we are the receiver so we respond with the callback ID 
    // todo: restrict who can receive message (last param) 
    iframe.contentWindow.postMessage(JSON.stringify(message), '*'); 
}); 

// when sending messages 
function phostMessage(iframe, obj, callback) { 
    obj.eventId = Math.random(); 
    callbacks[obj.eventId] = callback; 
    // todo: restrict who can receive message (last param) 
    iframe.contentWindow.postMessage(JSON.stringify(obj), '*'); 
} 

Tomo este concepto un poco más allá y utilizar una búsqueda de controlador de mensajes en el que el mensaje tiene el nombre de función de controlador deseado para evocar y transmitir un mensaje a . El manejador de mensajes también realiza una devolución de llamada que, cuando se completa, activa la devolución de llamada. La devolución de llamada simplemente tiene la lógica simple de llamar nuevamente al mensaje de correo nativo devolviendo su ID de devolución de llamada recibida.

Así que la última línea de código para el manejo de eventos de mensaje sería:

if (messageHandler[message.handler]) 
    messageHandler[message.handler](message, function() { 
    iframe.contentWindow.postMessage(JSON.stringify(message), '*'); 
    }); 
else 
    iframe.contentWindow.postMessage(JSON.stringify(message), '*'); 

que permite cosas asíncrono suceda.

Cuestiones relacionadas