2011-12-12 218 views
16

到目前为止,我只看到了postmessage的教程,其中一个窗口发送单一类型的消息,另一个窗口以单一方式解释消息。具有多种功能或自定义回调的PostMessage

如果我想在窗口之间有很多不同种类的交互,那么postmessage可以处理那个呢?

这是违背什么postmessage应该做的粮食?

例如,如果我想能够自定义回叫来回等等,该怎么办?

+0

不确定你的意思;你可以自由地检查收到的消息,并有条件地调用不同的代码等,所以... – Pointy

+0

迄今为止,我还没有看到任何的例子,并从我读过的,postmessage不能发送对象或任何东西,这意味着我不得不剖析字符串...只是看起来不太干净。 – johnnietheblack

回答

49

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

假设我们想传递用户ID,操作和用户名。字符串应该是这样的:

54|do_logout|chris

postMessage处理程序中,所传递的数据可以是对|字符splitdocs),那么可以根据需要所使用的消息的每个分段。

另一种方法是使用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只能处理字符串。 IE 10的postMessage未来计划未能找到任何讨论。此外,IE 8/9中存在一个已知的错误,该错误除了帧以外的其他内容都会被破坏postMessage。 (source)。

进入您的问题的具体方面 - 回调。除非你能够通过函数名称传递回调,否则没有办法传递一个函数;没有匿名功能给你。这与数据实际传递给处理程序的方式有关。在实践中,“不”支持对象作为数据,在幕后浏览器将传递的对象转换为字符串(序列化)。所有这一切说,那么,你应该明白,传递一个对象与在传递之前使用JSON到一个对象完全相同,只是在前一种情况下浏览器正在做自己的序列化(以及后续的非序列化),而使用后者的路由由您来序列化/反序列化。

外卖点这里:

  • 的postMessage仍然限制了跨浏览器支持
  • 为符合标准的浏览器新版本的趋势是允许除字符串
  • 对象的通道
  • 传递的对象将被序列化,所以没有函数引用允许
  • 最广泛的支持“在野外”仅用于字符串数据,这意味着你必须坚持使用字符串和“包”你的数据如上所示如果你想支持aw IDE多种用户代理
  • Internet Explorer将毁了每一个你曾经制定方案(包括家庭假期)

文档和参考

+5

很好的回答!祝愿我会为+10家庭假期评论 – johnnietheblack

+1

您可以随时在消息的任意一侧执行JSON编码和解码。 – Pointy

+1

即使使用JSON序列化,也不能传递函数引用(我相信OP是在之后的),因为它们不能被序列化。如果您使用JSON对对象进行编码(对于IE)或仅传递对象(对于Firefox> 6),则情况就是这样。如果您尝试在Firefox中传递函数,则会出现错误:错误:无法克隆该对象。:(太糟糕了,但这可能会更安全。) –

1

一个很简单的方法来触发回调不传递任何实际的代码是:

目标

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

来源

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

回调使用于PostMessage:非常有可能和非常有用

有一个很好的插件,我就发现npm called "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), '*'); 
} 

我将这个概念一点,并使用该消息具有所需的处理函数名,以唤起和传递消息到消息处理程序查找。消息处理程序也会进行回调,以便在完成时触发回调。回调只是简单的调用本地post消息的逻辑,再次发回其接收到的回叫id。

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

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

允许异步的东西发生。