使用postMessage进行Iframe跨域通信

最近遇到了一个问题:一个iframe页面需要和它的宿主页面进行通信,点击iframe的某个按钮iframe消失,主页面还可以向iframe传参。iframe页面和主页面是不同域名的页面, 这就涉及到跨域访问了,以前跨域资源访问都很麻烦,使用postMessage可以很方便安全地进行跨域通信。

postMessage

window.postMessage() 方法可以安全地实现跨源通信。通常,对于两个不同页面的脚本,只有当执行它们的页面位于具有相同的协议(通常为https),端口号(443为https的默认值),以及主机 (两个页面的模数 Document.domain设置为相同的值) 时,这两个脚本才能相互通信。window.postMessage() 方法提供了一种受控机制来规避此限制,只要正确的使用,这种方法就很安全。

window.postMessage() 方法被调用时,会在所有页面脚本执行完毕之后(e.g., 在该方法之后设置的事件、之前设置的timeout 事件,etc.)向目标窗口派发一个 MessageEvent 消息。 该MessageEvent消息有四个属性需要注意: message 属性表示该message 的类型; data 属性为 window.postMessage 的第一个参数;origin 属性表示调用window.postMessage() 方法时调用页面的当前状态; source 属性记录调用 window.postMessage() 方法的窗口信息。

语法

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

otherWindow

其他窗口的一个引用,比如iframe的contentWindow属性、执行window.open返回的窗口对象、或者是命名过或数值索引的window.frames。

message

将要发送到其他 window的数据.从 Gecko 6.0 (Firefox 6.0 / Thunderbird 6.0 / SeaMonkey 2.3)开始,参数 message被使用结构化克隆算法进行序列化。这意味着您可以将各种各样的数据对象安全地传递到目标窗口,而不必自己序列化它们。在此之前只能传字符串。

targetOrigin

通过窗口的origin属性来指定哪些窗口能接收到消息事件,其值可以是字符串”*“(表示无限制)或者一个URI.在发送消息的时候,如果目标窗口的协议、主机地址或端口这三者的任意一项不匹配targetOrigin提供的值,那么消息就不会被发送;只有三者完全匹配,消息才会被发送。

transfer 可选,太高级不常用

接下来根据我的需求来展示下postMessage的用法,DOM结构如下:

<!-- index.html -->
<!DOCTYPE html>
<html>
  <head>
    <title>Document</title>
  </head>
  <body>
   <iframe src="https://pc.xunlei.com/auth" id="authIframe"></iframe>
  </body>
</html>

点击iframe上的按钮iframe消失

iframe向主页面发送数据

// iframe
window.parent.postMessage(JSON.stringify({
  from: 'auth',
  event: 'close',
  code: 1
}), '*')

主页面接收数据

// index.html
 window.addEventListener('message', function (e) {
  try {
    let data = JSON.parse(e.data)
    if (data.from === 'auth' && data.event === 'close') {
      authIframe.style.display = 'none'
    }
  } catch (error) {
    console.log(error)
  }
})

主页面向iframe传参

主页面发送数据

在iframe加载完成后再发送数据

// index.html
let authIframe = document.getElementById('authIframe')
let params = {
  id: 1213
}

authIframe.addEventListener('load', function (e) {
  authIframe.contentWindow.postMessage(JSON.stringify(params), 'https://pc.xunlei.com')
})

iframe接收参数

// iframe
let params = {}
window.addEventListener('message', function (event) {
  params = JSON.parse(event.data);
})

总结

发送数据用postMessage,接收数据是监听message事件:element.addEventListener('message', callback)