20

到目前为止,我只看到过关于 postmessage 的教程,其中一个窗口发送一种消息,而另一个窗口仅以一种方式解释消息。

如果我想在窗口之间进行许多不同类型的交互怎么办,postmessage 可以处理吗?

这是否违背了 postmessage 应该做的事情?

例如,如果我希望能够来回发送自定义回调等怎么办?

4

4 回答 4

62

有几种方法可以将多部分消息传递给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 只能处理字符串。我无法找到有关postMessageIE 10 未来计划的任何讨论。此外,IE 8/9 中有一个已知错误,它会postMessage因帧以外的任何内容而中断。(来源)。

进入问题的特定方面 - 回调。除非您能够通过函数名传递回调,否则无法传递函数;没有适合您的匿名函数。这与数据实际传递给处理程序的方式有关。实际上,“不”支持将对象作为数据,在幕后浏览器正在将您传递的对象转换为字符串(序列化)。

综上所述,您应该明白,传递一个对象与在传递之前使用 JSON 到一个对象完全相同stringify,只是在前一种情况下,浏览器正在执行自己的序列化(以及随后的反序列化),而在后一种情况下,它会路由由您决定序列化/反序列化。

这里的要点:

  • postMessage 仍然有限的跨浏览器支持
  • 较新版本的符合标准的浏览器的趋势是允许除字符串之外的对象通过
  • 传递的对象将被序列化,因此不允许函数引用
  • “在野外”最广泛的支持是仅字符串数据,这意味着如果您想支持各种各样的用户代理,您必须坚持使用字符串并“打包”您的数据,如上所示
  • Internet Explorer 将毁掉你曾经制定的每一个计划(包括家庭假期)

文档和参考资料

于 2011-12-12T20:00:47.647 回答
4

使用 postMessage 进行回调:非常可能且非常有用

我在npm 上找到了一个不错的插件,名为 "silver-bullet"。它使用回调执行 postMessage 并使用 eventEmitter 来获取特定事件。这是很不错的。

但要实现这一点,我会做类似的事情......

phostMessage(iframe, someObj, callback);

你必须这样做:

  1. 您需要在通信的帧之间传递一个通用的回调 ID 。
  2. 发送者在每条消息上创建一个唯一的回调 ID,并将其存储在回调查找哈希中,以便在发送后找到回调。
  3. 消息的接收者只确保回调 ID 被发回
  4. 为此,所有通信的帧都使用相同的 JS 库。

这是一个非常基本的演示:

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), '*');

这允许异步的事情发生。

于 2015-06-25T16:01:45.087 回答
1

我最近遇到了同样的问题。经过几个小时的搜索,我遇到了post-robot。它由我开发paypal并解决了我的大部分问题,包括为postMessage.

它还支持在有效负载内传递函数。

您可以在此处查看介绍介绍后机器人

于 2019-03-19T10:15:45.193 回答
0

在不传递任何实际代码的情况下触发回调的一种非常简单的方法是:

目标

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

来源

target.postMessage('myCallback', 'http://www.example.com');
于 2012-08-03T20:03:18.157 回答