Javascript 具有多个函数或自定义回调的 PostMessage

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/8479911/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-24 06:09:25  来源:igfitidea点击:

PostMessage with multiple functions or custom callbacks

javascriptcross-domainpostmessage

提问by johnnietheblack

So far I've only seen tutorials for postmessage where one window sends a single kind of message, and the other window interprets the message in only a single way.

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

What if I want to have many different kinds of interactions between windows, can postmessage handle that?

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

Is that going against the grain of what postmessage is supposed to do?

这与 postmessage 应该做的事情背道而驰吗?

For example, what if I wanted to be able to send custom callbacks back and forth, etc?

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

回答by Chris Baker

There are a couple of ways to pass a multi-part message on to a postMessagehandler. The first (and less "clean" way) is to use a delimiter character, then pass your data through a string.

有几种方法可以将多部分消息传递给postMessage处理程序。第一种(不太“干净”的方法)是使用分隔符,然后通过字符串传递数据。

Let's say we wanted to pass a user ID, an action, and the users name. The string would look like this:

假设我们想要传递一个用户 ID、一个操作和用户名。该字符串将如下所示:

54|do_logout|chris

54|do_logout|chris

Within the postMessagehandler, the passed data can be split(docs) on the |character, then each segment of the message can be used as needed.

postMessage处理程序中,传递的数据可以splitdocs)在|字符上,然后可以根据需要使用消息的每个段。

Another route, instead of manually creating/splitting a string, is to use JSON (docs) to convert an object into a string on one side, and use JSON to convert back to an object in the handler.

另一种方法是使用 JSON ( docs) 在一侧将对象转换为字符串,并使用 JSON 转换回处理程序中的对象,而不是手动创建/拆分字符串。

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

... then in the handler:

...然后在处理程序中:

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

Be sure to test, though, as the JSONobject is not provided on all user agents, especially older ones. There are many (many, many) third-party libraries out there to shim JSON support, so don't let the lack of complete adoption scare you away - JSON is definitely a safe "moving forward" standard.

但是,请务必进行测试,因为JSON并非所有用户代理都提供该对象,尤其是较旧的用户代理。有很多(很多很多)第三方库来填充 JSON 支持,所以不要让缺乏完全采用吓跑你 - JSON 绝对是一个安全的“向前发展”标准。

Wouldn't it be nicer if we could just pass that object straightaway? Well, staring in Firefox 6 (source), the data you pass to a postmessage handler may be an object. The object will be serialized, so there are some concerns on that front, but:

如果我们可以直接通过那个对象不是更好吗?好吧,在 Firefox 6 ( source) 中,您传递给 postmessage 处理程序的数据可能是一个对象。该对象将被序列化,因此在这方面存在一些问题,但是:

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

A little nicer, eh? Unfortunately, current versions of IE will only deal with strings. I was not able to find any discussion on future plans regarding postMessagefor IE 10. Further, there is a known bug in IE 8/9 which breaks postMessagefor anything other than frames. (source).

好一点,嗯?不幸的是,当前版本的 IE 只能处理字符串。我找不到关于postMessageIE 10 的未来计划的任何讨论。此外,IE 8/9 中存在一个已知错误,它会postMessage因帧以外的任何内容而中断。(来源)。

Getting in to a specific aspect of your question - callbacks. Unless you're able to pass the callback by function name, there isn't a way to pass a function; no anonymous functions for you. This is related to the way the data is actually passed on to the handler. In practice, there "is not" support for objects as data, behind the scenes the browser is turning your passed object into a string (serialization).

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

All that said, then, you should understand that passing an object is exactly the same as using JSON to stringifyan object before passing, only in the former case the browser is doing its own serialization (and subsequent unserialization), whereas with the latter route it is up to you to serialize/unserialize.

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

The take-away points here:

这里的外卖点:

  • postMessage still has limited cross-browser support
  • The trend for newer versions of standards-compliant browsers is to allow passage of objects in addition to strings
  • The passed object will be serialized, so no function references allowed
  • The widest support "in the wild" is for string-only data, which means you'll have to stick with strings and "pack" your data as demonstrated above if you want to support a wide variety of user agents
  • Internet Explorer will ruin every plan you ever make (including family holidays)
  • postMessage 仍然具有有限的跨浏览器支持
  • 新版本的符合标准的浏览器的趋势是允许除字符串之外的对象通过
  • 传递的对象将被序列化,因此不允许函数引用
  • 最广泛的“in the wild”支持是仅字符串数据,这意味着如果您想支持各种用户代理,您必须坚持使用字符串并“打包”您的数据,如上所示
  • Internet Explorer 会毁掉你所做的每一个计划(包括家庭假期)

Documentation and References

文档和参考资料

回答by Jason Sebring

Callbacks with postMessage: very possible and very useful

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

There is a nice plugin I've found on npm called "silver-bullet". It does postMessage with callbacks and uses eventEmitter to get specific events as well. Its very nice.

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

But to implement this I would do something like...

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

phostMessage(iframe, someObj, callback);

You have to do this:

你必须这样做:

  1. You need a common callback IDpassed between frames communicating.
  2. The sender creates a unique callback IDon each message and stores it in a callback lookup hash to find the callback after sending.
  3. The receiverof the message only ensures the callback ID is sent back.
  4. All frames communicating use the same JS library for this.
  1. 您需要在帧通信之间传递一个通用回调 ID
  2. 发送者创建了一个独特的回调ID在回调查找散每封邮件,并将其存储发送后找到回调。
  3. 消息的接收者只确保回调 ID 被发回
  4. 为此,所有帧通信都使用相同的 JS 库。

Here's a very basic demonstration of that:

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

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

I take this concept a bit further and use a message handler lookup where the message has the desired handler function name to evoke and pass a message to. The message handler takes a callback as well that when completed fires the callback. The callback just has the simple logic of calling the native post message again sending back its received callback id.

我更深入地理解这个概念,并使用消息处理程序查找,其中消息具有所需的处理程序函数名称来调用和传递消息。消息处理程序也接受回调,当完成时触发回调。回调只是具有简单的逻辑,即再次调用本机 post 消息并将其接收到的回调 id 发回。

So the last line of code for the message event handling would be:

所以消息事件处理的最后一行代码是:

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

which allows asynchronous stuff to happen.

这允许异步的事情发生。

回答by Atishay Jain

I recently faced the same problem. After hours of searching i came across post-robot. It's developed by paypaland solved most of my problems including having a callback for postMessage.

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

It also supports passing functions inside the payload.

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

You can check out the introduction here Introducing post-robot

你可以在这里查看介绍介绍后机器人

回答by Jacob Rask

One pretty easy way to triggercallbacks without passing any actual code would be:

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

Target

目标

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

Source

来源

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