我们相信:世界是美好的,你是我也是。平行空间的世界里面,不同版本的生活也在继续...

本文依然说一下同设备不同页面跨域通信的解决方案,这个方案暂时称之为postMessage方案。不过和上一篇文章里面的频道广播的.postMessage()方法是不同的,其数据发送主体不一样。本文中的.postMessage()的主体是浏览器的窗口对象,而且这个对象的接收者和发送者身份切换,也是比较令人费解的。

苏南大叔:前端跨域通信,如何利用postMessage实现跨域页面通信? - postmessage-跨域页面通信
前端跨域通信,如何利用postMessage实现跨域页面通信?(图4-1)

这里是苏南大叔的程序如此灵动博客,记录苏南大叔和计算机代码的故事。本文描述前端技术里面不同页面通信的技术手段之一,本文中的postMessage方案,操作主体是浏览器的窗口对象,同时可以解决同设备不同页面的跨域问题。本文测试环境:谷歌浏览器@110.0.5481.77

页面间关系

和频道广播的方案所不同的一点是:本文的方案中,页面之间的关系非常重要。页面之间必须主动知道彼此的存在,否则无法通信。彼此知道的方式是:页面之间是相互打开的,那么就建立了页面之间的相互关系,进而为数据通信打下坚实的基础。

常见的调用方式,有页面一iframe页面二,或者页面一打开页面二。总结如下:

关系调用方式发送消息调用主体反向发送消息
iframeiframe id='f'document.getElementById("f").contentWindow.window.top.
openera = window.open('')a.window.opener.

对于window.open()的情况,存在着被浏览器拦截弹窗的可能性,应用场景相对有限。

发送消息

发送消息的代码是:

被发送消息的页面所在的window对象.postMessage(消息字符串或对象,域限制短语)

这个是很令人费解的发送方式:
发送者js代码” => 被发送者所在的浏览器对象 => .postMessage() => 消息对象(字符串或者js对象) => 然后表明一下消息传递的域限制

  • 被发送者所在的浏览器对象,这个就是 发送者页面 如何 找到 被发送者的页面,可能的方式是:通过iframecontentWindow,或者 window.open的返回id
  • 域限制,这个默认情况就是/,也就是说相同域名下,为了跨域,可以被这个参数改写成*。或者传递一个域名地址。

接收消息

接收消息的代码,这个就没有那么令人费解了,直接监听windowmessage事件即可。所以,写法有下面两种:

window.onmessage = function(event) {
  console.log(event.data);
}

或者

window.addEventListener('message', function(event) {
  console.log(event.data);
},false)
目标代码
发送者是谁event.origin
发送的消息是什么event.data

至于最后一个参数useCapture,是用于控制事件传递的方向的。参考文章:

域限制

接收者消息事件中有个event.origin,发送者的postMessage()的第二个参数是个域限制。两者有关,但是也并不完全一致。

首先,发送者有域限制的权利,保证非法的接收者拿不到数据:
发送者(虽然委托的是接收者所在的浏览器对象发送的)需要指定消息的接收者的域关系。不填写的话,就默认是/,这个比较好理解就是同域的意思。填写为*,就是表示跨域。如果写上精确的域名(比如``),那么就需要接收者页面自己做判别了。

.postMessage("消息")
.postMessage({msg:"消息",code:1000})
.postMessage("消息", "/")
.postMessage("消息", "*")
.postMessage("消息", "http://localhost/")

其次,接收者也有拒绝消息的权利,通过判断event.origin来自行判断是否接收当前消息。比如:

window.addEventListener('message', function(event) {
  if (event.origin.includes("http://localhost/")) {
    // ...
  }
});

测试代码

页面一:

<h1>页面一</h1>
<div id="message"></div>
<h2>页面一两种方式打开了页面二,两个页面二都返回了数据。</h2>
<iframe id="child" src="http://a/postmessage/2.html" style="width:500px;"></iframe>
<script>
  window.onload = function () {
    var target = "";
    target = document.getElementById('child').contentWindow;
    target.postMessage("苏南大叔", "*");
    var target = window.open("http://a/postmessage/2.html");
    setTimeout(function () {
      target.postMessage({msg:"sunan大叔",coin:888}, "http://a/postmessage/");
    }, 2000);
  };
  window.addEventListener(
    "message",
    function (event) {
      document.getElementById("message").innerHTML += event.origin + "=》" + event.data + "<br/>";
    }
  );
  window.onmessage = function(){
    document.getElementById("message").innerHTML += event.origin + "=》》" + event.data + "<br/>";
  }
</script>

苏南大叔:前端跨域通信,如何利用postMessage实现跨域页面通信? - 页面一发送数据
前端跨域通信,如何利用postMessage实现跨域页面通信?(图4-2)

页面二:

<h1>页面二</h1>
<div id="message"></div>
<script>
  window.addEventListener(
    "message",
    function (event) {
      console.log(event);
      if (event.origin.includes("http://localhost")) {
        if (window.top!=window.self) {
          document.getElementById("message").innerHTML += "iframe方式," + event.origin + " :" + event.data;
          window.top.postMessage(
            "页面2返回消息【iframe】","http://localhost/"
          );
        } else if (window.opener != null) {
          document.getElementById("message").innerHTML += "opener方式," + event.origin + " :" + event.data.msg + "," + event.data.coin;
          window.opener.postMessage("页面2返回消息【opener】," + event.data.coin, "*");
        }
      }
    }
  );
</script>

苏南大叔:前端跨域通信,如何利用postMessage实现跨域页面通信? - 页面二接收数据
前端跨域通信,如何利用postMessage实现跨域页面通信?(图4-3)

  • 支持本地file://
  • 两者之间可以在不同的域名下面,代码中可以指定作用域。
  • 页面间可以是iframe关系,也可以是opener关系。在对应的代码中有区分。

苏南大叔:前端跨域通信,如何利用postMessage实现跨域页面通信? - 运行效果
前端跨域通信,如何利用postMessage实现跨域页面通信?(图4-4)

相关文章

总结

本文描述的方案,就是数据发送接收主体有些混乱,页面之间的关系必须理清。同时,记得指明数据归属的域。域的问题,可能面临两个:一是数据不能发送出去(浏览器的安全策略层面),二是数据发送到了错误的域上(代码的数据安全逻辑)。

代码中的.postMessage的主体,更像是个第三方委托发送,如果增加个第三方委托的概念的话,这个发送关系就比较好理解了。单纯的理解为发送方和接收方的话,发送方的主体则是有些令人费解的。

如果本文对您有帮助,或者节约了您的时间,欢迎打赏瓶饮料,建立下友谊关系。
本博客不欢迎:各种镜像采集行为。请尊重原创文章内容,转载请保留作者链接。

 【福利】 腾讯云最新爆款活动!1核2G云服务器首年50元!

 【源码】本文代码片段及相关软件,请点此获取更多信息

 【绝密】秘籍文章入口,仅传授于有缘之人   js