HTML5 跨域通讯 API - window.postMessage
html
参考
MDN - Window.postMessage()跨域
Syntax
otherWindow.postMessage(message, targetOrigin, [transfer])浏览器
MDN 介绍的 window.postMessage() 是针对在一个页面使用 window.open() 动态打开新的页面而进行的跨域通讯,很是详细,Demo 也很实用,可是对我而言貌似还欠缺什么东西。框架
注意,在实现跨域通讯是,必须首先先得到其余域的window窗体对象函数
Tips
在 var targetPage = window.open('http://target.com') 打开新页面以后须要等到 http://target.com 页面加载完成以后才能进行 postMessage 跨域通讯,可是在跨域的状况下咱们是没法对 targetPage 进行 onload 事件监听的,因此这里只能作 延迟 setTimeout 或者 定时 setInterval 处理。 一样的,在页面内嵌入 iframe 页面的状况下,咱们也须要等到页面内的 iframe 加载完成以后进行 postMessage 跨域通讯。post
解决问题要从问题源头出发,我如今遇到的问题归根究底就是两个不一样域名的页面如何进行通讯?
浏览器的同源政策不容许跨域,然而 HTML5 API window.postMessage() 就是用来实现跨域通讯的。
那么通讯的原理是怎样的了?
若是有两个页面 PageA 和 PageB,PageA 页面内嵌入 iframe PageB,那么理论上是应该能够实现双向通讯的。
其实很是简单,就是 PageA 经过 window.postMessage() 发送一个信息给 PageB,PageB 在 window 上添加一个事件监听绑定 message 事件能够接收到来自任何不一样域名经过 postMessage 方法发送过来的信息,当 PageB 接收到 PageA 发送过来的信息时执行监听事件就 OK,在监听事件的 event 参数中包含了全部 message 事件接收到的相关数据。包括发送信息的内容 event.data,发送信息的域名 event.origin 等等。spa
一样的,在 PageA 内添加一个事件监听绑定 message 事件,在 PageB 内经过 postMessage 方法发送信息给 PageA 同样能够进行跨域通讯。code
Tips
咱们能够经过 event.origin 来过滤掉来自其余未知站点发送过来的 message 事件信息,防止跨站攻击!orm
大概就是以上的思考,而后继续写 Demo...htm
干货代码PageA
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Page A</title> </head> <body> <h1>This is Page A</h1> <button id="openNewWindowBtn" type="button">Open New Window</button> <button id="postMessageBtn" type="button">Post Message</button> <p id="message"></p> <iframe id="receiverIframe" src="http://192.168.198.157:3000/pageB.html" frameborder="1" width="800" height="500"></iframe> <script> window.onload = function() { var receiver = document.getElementById('receiverIframe').contentWindow; var postBtn = document.getElementById('postMessageBtn'); var openBtn = document.getElementById('openNewWindowBtn'); var messageEle = document.getElementById('message'); function sendMessage() { receiver.postMessage('Hello Page B.. This is page A.. You are my iframe', 'http://192.168.198.157:3000'); } function openNewWindow() { var pageB = window.open('http://192.168.198.157:3000/pageB.html'); setTimeout(function() { pageB.postMessage('Hello Page B.. This is Page A.. (form PageA window.open())', 'http://192.168.198.157:3000'); }, 500) } function receiveMessage(event) { console.log(event); if (event.origin !== 'http://192.168.198.157:3000') return; messageEle.innerHTML = "Message Received: " + event.data; } postBtn.addEventListener('click', sendMessage, false); openBtn.addEventListener('click', openNewWindow, false); window.addEventListener('message', receiveMessage, false); } </script> </body> </html>
PageB
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Page B</title> </head> <body> <h1>This is Page B</h1> <button id="postMessageBtn" type="button">Post Message</button> <p id="message"></p> <script> window.onload = function() { var postBtn = document.getElementById('postMessageBtn') var messageEle = document.getElementById('message'); function receiveMessage(event) { console.log(event); if (event.origin !== 'http://192.168.198.157:8000') return; messageEle.innerHTML = "Message Received: " + event.data; // 接收 PageA 的任何消息都自动回复并加上时间戳 event.source.postMessage('Hello Page A.. This is page B.. (from PageB autoreply) timestamp = ' + new Date().getTime(), event.origin); } function sendMessage() { // 这里须要特别注意!!! // 直接打开 PageB (当前页面) 是没法向 PageA 发送跨域信息的!!! // 只有当 PageB (当前页面) 处于 PageA 页面内的 iframe 中的时候才能发送跨域信息 // 并且此处不能使用 window.postMessage() // 由于 PageB (当前页面) 是 PageA 页面内嵌入的 iframe // 此时 PageB 的 window 指向的是 PageA 内 iframe 框架内的 window // 而当前状况须要指向父级 window (即 top 或者 parent) 才能进行 postMessage top.postMessage('Hello Page A.. This is page B..', 'http://192.168.198.157:8000'); } postBtn.addEventListener('click', sendMessage, false); window.addEventListener('message', receiveMessage, false); } </script> </body> </html>
踩过的坑
PageB 须要特别注意的地方!!!
直接在浏览器中打开 PageB 页面是没法向 PageA 页面发送跨域信息的!!!
PageB 页面的 receiveMessage 方法自动回复了全部来自 PageA 页面的 postMessage 信息而且加上了时间戳。
为何 PageB 页面内的 sendMessage 方法使用的是 top.postMessage() 发送跨域信息???
答案就在下面的结论中
Tips
若是不是使用 window.open() 打开的页面或者 iframe 嵌入的页面,就跟当前页面扯不上任何关系,是没法使用 window.postMessage() 进行跨域通讯的!!!
描述的貌似不是很清楚,举个栗子:
若是你打开浏览器,输入一个页面地址 PageA,而后打开一个新的标签页,又输入一个页面地址 PageB,那么这两个页面是不管如何都不能使用 window.postMessage() 来进行跨域通讯的,他们并无任何血缘关系...
一样,打开浏览器,输入一个页面地址 PageA,而后经过 PageA 动态打开 PageB (固然,不是经过 PageA 内的 a 标签连接打开),或者 PageA 内嵌入了 iframe PageB,那么这个时候就厉害了,它两有血缘关系啦!PageB 这个时候是否是就至关因而 PageA 是崽崽?是由于有了 PageA,因此才有了 PageB 的出现。而后理所固然的,PageA 拥有了控制 PageB 的某些权限,其中就包括 window.postMessage()。
得出结论
window.postMessage() 中的 window 究竟是什么呢?
A:始终是你要通讯的目标页面的 window
PageA 页面内嵌入 iframe PageB 页面
PageA 页面向 PageB 页面发送跨域信息,window 为 PageB 页面的 window,即 iframe.contentWindow。
PageB 页面向 PageA 页面发送跨域信息,window 为 PageA 页面的 window,即 top 或者 parent。
PageA 页面内代码使用 window.open() 打开 PageB 页面
PageA 页面向 PageB 页面发送跨域信息,window 为 var pageB = window.open('http://192.168.197.157:3000/pageB.html') 中的变量 pageB。
PageB 页面没法主动给 PageA 页面发送跨域信息,必须先接收到 PageA 页面发送过来的 message 而后再经过 event.source 发送给 PageA,没错... 此时的 window 就是 event.source,即 PageA 的 window
最后最后
请不管如何在监听 message 事件的函数内对 event.origin 进行过滤,否则来自未知站点的 window.postMessage() 能够对你的站点随心所欲。