HTML5 postMessage 跨域交换数据

前言

  以前简单讲解了利用script标签(jsonp)以及iframe标签(window.namelocation.hash)来跨域交换数据,今天咱们来学习一下HTML5的api,利用postMessage来跨域交换数据。和前面一些方式交换数据方式不一样的是,利用postMessage不能和服务端交换数据只能在两个窗口(iframe)之间交换数据,废话很少说,咱们直接进入实战。javascript

实战postMessage

  • overview

  上文中说,postMessage是用于两个窗口(iframe)之间交换数据的,若是咱们同时打开着百度和谷歌两个页面,是否是说这二者之间就能够通讯了?No,no,no,事实并不是如此,就算百度和谷歌俩页面有通讯的意愿也不行。两个窗口能通讯的前提是,一个窗口以iframe的形式存在于另外一个窗口,或者一个窗口是从另外一个窗口经过window.open()或者超连接的形式打开的(一样能够用window.opener获取源窗口);换句话说,你要交换数据,必须能获取目标窗口(target window)的引用,否则两个窗口之间毫无联系,想通讯也无能为力。html

  既然是H5家族的,咱们也得观望下它被广大浏览器的接受程度(具体细节check can I use postMessage),能够看到接受程度仍是至关高的:html5

  而postMessage的使用方式也至关简单:java

otherWindow.postMessage(message, targetOrigin, [transfer]);

  otherWindow是对接收方窗口的引用,通常能够是如下几种方式:web

window.frames[0].postMessage
document.getElementsByTagName('iframe')[0].contentWindow
window.opener.postMessage
event.source.postMessage
window.open 返回的引用
...

  而message顾名思义就是发送的数据内容,支持字符串、数字、json等几乎全部形式的数据(详见The structured clone algorithmjson

  targetOrigin是接收方的URI(协议+主机+端口),也能够是url形式,但以后的内容(形如xx.html)会自动忽略;用通配符*能够指定全部域,可是切记不要用(for security)。api

  transfer可省略,没看懂是啥意思...之后有须要的时候再研究跨域

  而接受方窗口通常监听message事件,详见下面的例子。浏览器

  • window <-> iframe

  假设index页面有个iframe(不一样源),咱们要给iframe发送数据,而iframe获得数据后也发送数据给top window,表示“我"获得数据了。直接看源码(思考如何发送and如何接收):安全

<!-- http://localhost:81/fish/index.html -->
<script type="text/javascript">
  // 页面加载完后才能获取dom节点(iframe)
  window.onload = function(){
    // 向目标源发送数据
    document.getElementsByTagName('iframe')[0].contentWindow.postMessage({"age":10}, 'http://localhost:8080');
  };

  // 监听有没有数据发送过来
  window.addEventListener('message', function(e) {
      console.log(e);
  });
</script>
<iframe src="http://localhost:8080/index.html"></iframe>
<!-- http://localhost:8080/index.html -->
<script type="text/javascript">
  // 监听有没有数据发送过来
  window.addEventListener('message', function(e){
      // 判断数据发送方是不是可靠的地址
      if(e.origin !== 'http://localhost:81')
        return;
    // 打印数据格式
    console.log(e);
    // 回发数据
    e.source.postMessage('hello world', e.origin);
  }, false);
</script>

  咱们截图看看打印的东西究竟长什么样(index页面传给iframe的数据):

  红框标出的是三个最重要的属性,data顾名思义就是传输的数据了;origin就是发送消息窗口的源(URI 协议+主机+端口);而source就能引用发送消息的窗口对象(能够用它来引用发送窗口进行消息回传)。

   在消息接收端监听能够监听message事件(代码如上),固然若是要兼容坑爹的ie确定要用attachEvent。这里不推荐使用window.onmessage,兼容性不大好(好比不能兼容低版本ff)。

  • window <-> window

  说完了跟同一页面中的iframe的数据交换,再来讲说两个窗口之间的数据交换。咱们知道用window.open()能够打开一个新的窗口,而若是两个窗口同源,则两个窗口的通讯将会很是简单,咱们能够经过window.opener.functionName在新窗口中调用原来窗口的方法(和变量)。可是若是两个窗口不一样源,这样的操做将会变得很艰难,幸运的是H5给咱们提供了postMessage,使得window.opener.postMessage()不会报错!demo很简单:

<!-- http://localhost:81/fish/index.html -->
<script type="text/javascript">
  // 打开一个新的窗口
  var popup = window.open('http://localhost:8080/index.html');

  /// When the popup has fully loaded, if not blocked by a popup blocker:
  setTimeout(function() {
      // 当前窗口向目标源传数据
    popup.postMessage({"age":10}, 'http://localhost:8080');
  }, 1000);
</script>
<!-- http://localhost:8080/index.html -->
<script type="text/javascript"> 
  // 设置监听,若是有数据传过来,则打印
  window.addEventListener('message', function(e) {
    console.log(e);
    // console.log(e.source === window.opener);  // true
  });
</script>

  这里要设置一个定时器的缘由是向目标窗口发送数据必须等目标窗口彻底加载完,也就是说要在目标窗口中先设置好“监听器”,发送窗口发的数据才能被监听到,因此给了个定时器delay,而由于加载时间的不肯定因此定时器的delay值也不能肯定;另一个可行的办法是当目标页面加载完后,发个消息个源页面(postMessage),而源页面收到消息,再用postMessage发送消息给目标页面。

安全顾虑

  提到跨域交换数据,条件反射都会问一句,安全吗?对于postMessage,答案是确定的。

  postMessage采用的是“双向安全机制”。发送方发送数据的时候,会确认接受方的源(因此最好不要用*),而接受方监听到message事件后,也能够用event.origin判断是否来自于正确可靠的发送方。

参考

  1. Window.postMessage()
  2. HTML5的postMessage使用记要
  3. Cross-window messaging with postMessage
  4. HTML5 postMessage 和 onmessage API 详细应用
相关文章
相关标签/搜索