到目前为止,我只看到过关于 postmessage 的教程,其中一个窗口发送一种消息,而另一个窗口仅以一种方式解释消息。
如果我想在窗口之间进行许多不同类型的交互怎么办,postmessage 可以处理吗?
这是否违背了 postmessage 应该做的事情?
例如,如果我希望能够来回发送自定义回调等怎么办?
到目前为止,我只看到过关于 postmessage 的教程,其中一个窗口发送一种消息,而另一个窗口仅以一种方式解释消息。
如果我想在窗口之间进行许多不同类型的交互怎么办,postmessage 可以处理吗?
这是否违背了 postmessage 应该做的事情?
例如,如果我希望能够来回发送自定义回调等怎么办?
有几种方法可以将多部分消息传递给postMessage
处理程序。第一种(也是不太“干净”的方式)是使用分隔符,然后通过字符串传递数据。
假设我们想要传递一个用户 ID、一个操作和用户名。字符串如下所示:
54|do_logout|chris
在postMessage
处理程序中,传递的数据可以split
(文档)在|
字符上,然后可以根据需要使用消息的每个段。
另一种方法是使用 JSON ( docs ) 将对象转换为一侧的字符串,并使用 JSON 转换回处理程序中的对象,而不是手动创建/拆分字符串。
var pass_data = {
'name':'Chris',
'id':54,
'action':'do_logout'
};
target_window.postMessage(JSON.stringify(pass_data), "http://www.example.net");
...然后在处理程序中:
function (event) {
var pass_data = JSON.parse(event.data);
}
但是请务必进行测试,因为JSON
并非所有用户代理都提供该对象,尤其是较旧的用户代理。有很多(很多很多)第三方库来支持 JSON,所以不要让缺乏完全采用吓跑你 - JSON 绝对是一个安全的“前进”标准。
如果我们可以直接传递那个对象不是更好吗?好吧,在 Firefox 6 ( source ) 中,您传递给 postmessage 处理程序的数据可能是一个对象。该对象将被序列化,因此在这方面存在一些问题,但是:
var pass_data = {
'name':'Chris',
'id':54,
'action':'do_logout'
};
target_window.postMessage(pass_data, "http://www.example.net");
好一点,嗯?不幸的是,当前版本的 IE 只能处理字符串。我无法找到有关postMessage
IE 10 未来计划的任何讨论。此外,IE 8/9 中有一个已知错误,它会postMessage
因帧以外的任何内容而中断。(来源)。
进入问题的特定方面 - 回调。除非您能够通过函数名传递回调,否则无法传递函数;没有适合您的匿名函数。这与数据实际传递给处理程序的方式有关。实际上,“不”支持将对象作为数据,在幕后浏览器正在将您传递的对象转换为字符串(序列化)。
综上所述,您应该明白,传递一个对象与在传递之前使用 JSON 到一个对象完全相同stringify
,只是在前一种情况下,浏览器正在执行自己的序列化(以及随后的反序列化),而在后一种情况下,它会路由由您决定序列化/反序列化。
这里的要点:
文档和参考资料
window.postMessage
:https ://developer.mozilla.org/en/DOM/window.postMessageString.split
:https ://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/String/splitJSON.stringify
:https ://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/JSON/stringifyJSON.parse
:https ://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/JSON/parse我在npm 上找到了一个不错的插件,名为 "silver-bullet"。它使用回调执行 postMessage 并使用 eventEmitter 来获取特定事件。这是很不错的。
但要实现这一点,我会做类似的事情......
phostMessage(iframe, someObj, callback);
你必须这样做:
这是一个非常基本的演示:
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), '*');
}
我将这个概念更进一步,并使用消息处理程序查找,其中消息具有所需的处理程序函数名称来调用和传递消息。消息处理程序也接受一个回调,完成后触发回调。回调只是具有简单的逻辑,即再次调用本机发布消息并将其接收到的回调 id 发回。
所以消息事件处理的最后一行代码是:
if (messageHandler[message.handler])
messageHandler[message.handler](message, function() {
iframe.contentWindow.postMessage(JSON.stringify(message), '*');
});
else
iframe.contentWindow.postMessage(JSON.stringify(message), '*');
这允许异步的事情发生。
我最近遇到了同样的问题。经过几个小时的搜索,我遇到了post-robot。它由我开发paypal
并解决了我的大部分问题,包括为postMessage
.
它还支持在有效负载内传递函数。
您可以在此处查看介绍介绍后机器人
在不传递任何实际代码的情况下触发回调的一种非常简单的方法是:
目标
var callbacks = {
myCallback: function() { doSomething(); }
};
window.addEventListener('message', function (ev) {
// origin checking etc
callbacks[ev.data]();
}, false);
来源
target.postMessage('myCallback', 'http://www.example.com');