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

时间:2011-12-12 19:43:31

标签: javascript cross-domain postmessage

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

如果我想在windows之间进行多种不同的交互,那么postmessage可以处理吗?

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

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

4 个答案:

答案 0 :(得分:54)

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

假设我们想要传递用户ID,操作和用户名。字符串看起来像这样:

54|do_logout|chris

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

另一种路由,而不是手动创建/拆分字符串,是使用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到stringify对象完全相同,只是在前一种情况下,浏览器正在进行自己的序列化(以及随后的反序列化) ),而后一种方法则由你来序列化/反序列化。

这里的外卖点是:

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

文档和参考资料

答案 1 :(得分:3)

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

我在npm called "silver-bullet"找到了一个不错的插件。它使用回调执行postMessage并使用eventEmitter来获取特定事件。非常好。

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

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

你必须这样做:

  1. 您需要在通信帧之间传递公共回调ID
  2. 发件人在每封邮件上创建一个唯一的回调ID ,并将其存储在回调查找哈希中,以便在发送后查找回调。
  3. 邮件的收件人仅确保回拨ID已发回
  4. 所有通信的帧都使用相同的JS库。
  5. 以下是一个非常基本的演示:

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

    我更进一步使用这个概念并使用消息处理程序查找,其中消息具有所需的处理函数名称以唤起并传递消息。消息处理程序也会进行回调,完成后会触发回调。回调只是具有调用本机发布消息的简单逻辑,再次发回其收到的回调ID。

    因此,消息事件处理的最后一行代码是:

    function onLoad() {
            document.addEventListener("deviceready", onDeviceReady, false);
        }
        function onDeviceReady() {
    
        }
    
    var map;
    function initialize() {
    
         var mapOptions = {
        zoom: 14  
      };
         map = new google.maps.Map(document.getElementById('map-canvas'),
          mapOptions);
    
    
      // Try HTML5 geolocation
      if(navigator.geolocation) {
        navigator.geolocation.getCurrentPosition(function(position) {
          var pos = new google.maps.LatLng(position.coords.latitude,
                                           position.coords.longitude);
    
            var infowindow = new google.maps.InfoWindow({
              content: 'contentstring'
           });
    
    
            var marker1 = new google.maps.Marker({
               position: pos,
               map: map,
               title: 'ciao!'
    
         }); 
    
        google.maps.event.addListener(marker1, 'click', function() {
        infowindow.open(map,marker1);
        });
    
    
        var marker2 = new google.maps.Marker({
               position: new google.maps.LatLng( 45.4353135, 12.135795400000006 ),
               map: map,
               title: 'ciao!'
    
         }); 
    
    
    
          map.setCenter(pos);
        }, function() {
          handleNoGeolocation(true);
        });
      } else {
        // Browser doesn't support Geolocation
        handleNoGeolocation(false);
      }
    }
    
    function handleNoGeolocation(errorFlag) {
      if (errorFlag) {
        var content = 'Error: The Geolocation service failed.';
      } else {
        var content = 'Error: Your browser doesn\'t support geolocation.';
      }
    
      var options = {
        map: map,
        position: new google.maps.LatLng(60, 105),
        content: content
      };
    
      var infowindow = new google.maps.InfoWindow(options);
      map.setCenter(options.position);
    }
    google.maps.event.addDomListener(window, 'load', initialize);
    

    允许异步内容发生。

答案 2 :(得分: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');

答案 3 :(得分:0)

我最近遇到了同样的问题。经过数小时的搜索,我遇到了post-robot。它由paypal开发,解决了我的大多数问题,包括为postMessage回调。

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

您可以在此处Introducing post-robot

查看介绍。