跨页面通信之postMessage

最近要写的一个页面内嵌了一个iframe,引入的页面是其他域名的页面。复页面需要获取子页面iframe里面的信息,这就需要子页面像父页面传值,首先想到了用iframe间的通信。

// 子页面
<div id='child'>i am a child</div>
    <script>
        window.onload = function() {
            var parentDom = parent.window.document.getElementById('parent');
            setTimeout(() => {
                parentDom.innerHTML = 'chang parent'
            }, 1000);
        }
    </script>
// 父页面
<h1 id="parent">i am parent</h1>
    <iframe id='iframe' src="./child.html" frameborder="0"></iframe>
    <script>
        window.onload = function() {
            var dom = document.getElementById('iframe');
            var child = dom.contentWindow.document.getElementById('child');
            child.innerHTML =' hello world'
            window.frames[0].document.getElementById('child').innerHTML = 'child2'
        }
    </script>

以上代码块是父子页面互相通信改变值的demo。
但是以上通信存在一个问题,如果父子页面属于不同的域,由于浏览器的同源政策,相互通信则比较麻烦。虽然可以在子页面中嵌入一个代理页面作为中转站通信,但是处理起来比较麻烦。经请教可以用H5的API postMessage进行不同域页面间的通信,对此做一个总结。

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

语法
1.发送postMessage消息:

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

otherWindow是需要发送消息的源窗口。源窗口可以是页面中的iframe窗口,也可以是通过window.open()打开的窗口。或者是命名过或数值索引的window.frames

// 页面中的iframe窗口
  var iframe = document.getElementById('my-iframe');
  var win = iframe.documentWindow;
// 弹窗
  var win = window.open();
// iframe窗口的父窗口
  var win = window.parent;

获取源窗口后,即可向目标窗口发送数据message,其中message会被结构化克隆算法序列化,也就是我们无需手动序列化数据传输到目标窗口。

targetOrigin用以指定哪些窗口可以接收消息,可以是字符串"*"(表示无限制)或者一个URI。如果目标窗口的协议、主机地址或端口这三者的任意一项不匹配targetOrigin提供的值,那么消息就不会被发送;只有三者完全匹配,消息才会被发送。因此如果我们知道需要向哪些窗口传输消息时,一定要指定targetOrigin,避免数据泄露。

window.parent.postMessage('i am click!', 'http://localhost:8081/parent3.html');

2.接收postMessage消息
在目标窗口注册message事件,绑定监听函数,监听函数的参数内附有传递来的消息。

 window.addEventListener('message',function(e){
     alert(e.data);
 },false);

其中e的内容如下:
跨页面通信之postMessage_第1张图片
其中origin是消息发送窗口的origin,我们可以在注册的回调函数内origin进行判断,然后决定是否做出回应。data是发送的数据内容,source是消息发送窗口的window对象引用。

因此在不同域中,嵌入的子页面iframe向父窗口发送消息具体demo如下。

// http://localhost:8081/parent3.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
    <div>
        parent
    </div>
    <iframe src="http://local.study.baidu.com:8084/child3" frameborder="0"></iframe>
    <script>
        window.addEventListener('message',function(e){
            alert(e.data);
        },false);
    </script>
</body>
</html>
// http://local.study.baidu.com:8084/child3.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
    <div>child3</div>
    <button>hello world</button>
    <script>
        window.onload = function () {
            var button = document.getElementsByTagName('button')[0];
            var count = 1;
            button.addEventListener('click', function() {
                window.parent.postMessage('i am click!' + (++count), 'http://localhost:8081/parent3.html');
            });
        }
    </script>
</body>
</html>

有一些我们需要注意的地方:
1.postMessage使用不当,会导致一些安全问题,需要我们注意:用于接收消息的任何事件监听器必须首先使用origin和source属性来检查消息的发送者的身份。 这不能低估:无法检查origin和source属性会导致跨站点脚本攻击。
2.如果不是使用 window.open() 打开的页面或者 iframe 嵌入的页面,无法使用 window.postMessage() 进行跨域通信的。CDN对此说的很明白,从广义上讲,一个窗口可以获得对另一个窗口的引用(比如 targetWindow = window.opener),然后在窗口上调用 targetWindow.postMessage() 方法分发一个 MessageEvent 消息。 两个不同页面是无法获取对方的window的,因此无法使用postMessage通信。
3.另外需要注意的是pageA使用window.open()打开的pageB,是无法主动向pageA发送消息的,必须先接收到 PageA 页面发送过来的 message 然后再通过 event.source 发送给 PageA,此时的 window 就是 event.source,即 PageA 的 window。

你可能感兴趣的:(javascript,javascript)