javascript - sure - PostMessage con múltiples funciones o devoluciones de llamada personalizadas
postmessage iframes (4)
Callbacks con postMessage: muy posible y muy útil
Hay un buen complemento que he encontrado en npm llamado "bala de plata" . Realiza el envío de mensajes con devoluciones de llamada y utiliza eventEmitter para obtener eventos específicos también. Es muy bonito.
Pero para implementar esto haría algo como ...
phostMessage(iframe, someObj, callback);
Tienes que hacer esto:
- Necesita un ID de devolución de llamada común que se transmita entre marcos que se comunican
- El remitente crea un ID de devolución de llamada único 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.
- El receptor del mensaje solo garantiza que se devuelva la ID de devolución de llamada .
- Todos los marcos que se comunican utilizan la misma biblioteca JS para esto.
Aquí hay una demostración muy básica de eso:
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 lejos y uso una búsqueda de manejador de mensajes donde el mensaje tiene el nombre de la función del manejador deseado para evocar y pasar un mensaje. El manejador de mensajes también recibe una devolución de llamada que, cuando se completa, activa la devolución de llamada. La devolución de llamada solo tiene la lógica simple de llamar nuevamente al mensaje de la publicación nativa y devolver su ID de devolución de llamada recibida.
Entonces, la última línea de código para el manejo del evento 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), ''*'');
lo que permite que ocurran cosas asíncronas.
Hasta ahora solo he visto tutoriales para mensajes de correo electrónico donde una ventana envía un solo tipo de mensaje, y la otra ventana interpreta el mensaje de una sola manera.
¿Qué pasa si quiero tener muchos tipos diferentes de interacciones entre ventanas, puedo manejar el mensaje después de un mensaje?
¿Eso va en contra de lo que se supone que debe hacer el mensaje posterior?
Por ejemplo, ¿qué pasa si quisiera poder enviar devoluciones de llamadas personalizadas, etc.?
Recientemente me enfrenté al mismo problema. Después de horas de búsqueda me encontré con post-robot . Está desarrollado por paypal
y solucionó la mayoría de mis problemas, incluido tener una devolución de llamada para postMessage
.
También soporta funciones de paso dentro de la carga útil.
Puedes ver la introducción aquí Presentando post-robot
Una manera bastante fácil de activar devoluciones de llamada sin pasar ningún código real sería:
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'');
Hay un par de formas de pasar un mensaje de varias partes a un controlador de postMessage
. La primera forma (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 cuerda se vería así:
54|do_logout|chris
Dentro del controlador postMessage
, los datos pasados se pueden split
( docs ) en el |
carácter, entonces cada segmento del mensaje se puede utilizar según 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 volver a convertir un objeto en el controlador.
var pass_data = {
''name'':''Chris'',
''id'':54,
''action'':''do_logout''
};
target_window.postMessage(JSON.stringify(pass_data), "http://www.example.net");
... luego en el controlador:
function (event) {
var pass_data = JSON.parse(event.data);
}
Sin embargo, asegúrese de probar, ya que el objeto JSON
no se proporciona en todos los agentes de usuario, especialmente los más antiguos. Hay muchas (muchas, muchas) bibliotecas de terceros para apoyar el soporte JSON, así que no deje que la falta de adopción completa lo asuste. JSON es definitivamente un estándar seguro de "avance".
¿No sería mejor si pudiéramos pasar ese objeto de inmediato? Bueno, al mirar en Firefox 6 ( source ), los datos que pasa a un controlador de mensaje posterior pueden ser un objeto. El objeto se serializará, 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 tratarán con cadenas. No pude encontrar ninguna discusión sobre los 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 una manera de pasar una función; No hay funciones anónimas para ti. Esto está relacionado con la forma en que los datos se pasan realmente al controlador. En la práctica, "no hay" soporte para objetos como datos, detrás de la escena, el navegador está convirtiendo su objeto pasado en una cadena (serialización).
Dicho todo esto, entonces, debe entender que pasar un objeto es exactamente lo mismo que usar JSON para stringify
un objeto antes de pasarlo, solo en el primer caso el navegador está realizando su propia serialización (y subsiguiente deserialización), mientras que con la última ruta depende de usted para serializar / unserialize.
Los puntos para llevar aquí:
- postMessage todavía tiene soporte limitado para varios navegadores
- La tendencia para las versiones más nuevas de navegadores compatibles con los estándares es permitir el paso de objetos además de cadenas
- El objeto pasado se serializará, por lo que no se permiten referencias de funciones
- El soporte más amplio "en la naturaleza" es para datos de solo cadena, lo que significa que tendrá que atenerse a las cadenas y "empaquetar" sus datos como se demostró anteriormente si desea admitir una amplia variedad de agentes de usuario
- Internet Explorer arruinará todos los planes que hagas (incluidas las vacaciones familiares)
Documentación y Referencias
-
window.postMessage
: source -
String.split
: docs - IE 8/9 Notas de soporte de HTML5: source
-
JSON.stringify
: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/JSON/stringify -
JSON.parse
: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/JSON/parse