【JavsScript】父子页面之间跨域通讯的方法

  因为同源策略的限制,JavaScript跨域的问题,一直是一个比较棘手的问题,为了解决页面之间的跨域通讯,你们煞费苦心,研究了各类跨域方案。以前也有小网同窗分享过一篇“跨域,再也不纠结” 开始照着尝试时仍是有些不够明白的地方,深刻了解以后,这里给你们补充一点更具体的作法。html

  先来看看哪些状况下才存在跨域的问题:html5

  其中编号六、7两种状况同属于主域名相同的状况,能够设置domain来解决问题,今天就不讨论这种状况了。 对于其余跨域通讯的问题,我想又能够分红两类:chrome

  其一(第一种状况)是a.com下面的a.js试图请求b.com下某个接口时产生的跨域问题。跨域

  其二(第二种状况)是当a.com与b.com下面的页面成父子页面关系时试图互相通讯时产生的跨域问题,典型的应用场景如a.com/a.html使用iframe内嵌了b.com/b.html,你们都知道a.html内的js脚本试图访问b.html时是会被拒绝的,反之亦然。浏览器

  第一种状况,目前主流的方案是JSONP,高版本浏览器支持html5的话,还可使用XHR2支持跨域通讯的新特性。安全

  第二种状况,目前主要是经过代理页面或者使用postMessageAPI来作,这也是今天要讨论的话题。第二种状况,有这样一些相似的案例:a.com/a.html使用iframe内嵌了b.com/b.html,如今但愿iframe的高度能自动适应b.html的高度,使iframe不要出现滚动条。咱们都知道跨域了,a.html是没办法直接读取到b.html的高度的,b.html也没办法把本身的高度告诉a.html。 直接说能够用代理页面的方法搞定这个问题吧,可是怎么代理法,先来看下面这张图:服务器

  b.html与a.html是不能直接通讯的。咱们能够在b.html下面再iframe内嵌一个proxy.html页面,由于这个页面是放在a.com下面的,与a.html同域,因此它实际上是能够和a.html直接通讯的,假如a.html里面有定义一个方法_callback,在proxy.html能够直接top._callback()调用它。可是b.html自己和proxy.html也是不能直接通讯的,所谓代理页面的桥梁做用怎么实现呢? b.html内嵌proxy.html是经过一段相似下面这样的代码: <iframe id=”proxy” src=”a.com/proxy.html” name=”proxy” frameborder=”0″ width=”0″ height=”0″></iframe> 这个iframe的src属性b.html是有权限控制的。若是它把src设置成a.com/proxy.html?args=XXX,也就是给url加一个查询字符串,proxy.html内的js是能够读取到的。对的,这个url的查询字符串就是b.html和proxy.html之间通讯的桥梁,美中不足的是每次通讯都要重写一次url形成一次网络请求,这有时会对服务器及页面的运行效率产生很大的影响。同时因为参数是经过url来传递的,会有长度和数据类型的限制,搜集的资料显示:网络

  • IE浏览器对URL的长度现限制为2048字节。
  • 360极速浏览器对URL的长度限制为2118字节。
  • Firefox(Browser)对URL的长度限制为65536字节。
  • Safari(Browser)对URL的长度限制为80000字节。
  • Opera(Browser)对URL的长度限制为190000字节。
  • Google(chrome)对URL的长度限制为8182字节。

  上面的方法,经过迂回战术实现了b.html跟a.html通讯,可是倒过来,a.html怎么跟b.html通讯呢?嵌入在b.html里面的proxy.html能够用top快速的联系上a.html,可是要想让a.html找到proxy.html就不容易了,夹在中间的 b.html生生把它们分开了,a.html无法让b.html去找到proxy.html而后返回给它。只能采用更迂回的战术了。 顺着前面b.html到a.html的通讯过程,逆向的想一下,虽然a.html没有办法主动找到proxy.html,可是proxy.html能够反过来告诉a.html它在哪里: 在proxy.html加这么一段脚本:dom

  也就是必须由proxy.html先主动发送一个消息给a.html,a.html获得proxy.html页面window的引用,就能够反过来向它发送请求了。 如今a.html能够把消息发给proxy.html了,可是proxy.html怎么把消息转送到b.html?彷佛这才是难点,由于它们之间才真正有着“跨域”这一道鸿沟。 这回咱们再也不用前面那个iframe内嵌代理页面的方法再在proxy.html内嵌一个b.com下面的代理页面了,这样实在会给人感受嵌的太深了,四层。可是为了跨越这道鸿沟,b.com下面也加一个代理页面是免不的。不过如今咱们要利用一下window.name。window.name有一个特性,就是页面在同一个浏览器窗口(标签页)中跳转时,它一直存在并且值不会改变。好比咱们在a.html中设置了window.name=”a”,而后location.href=”http://b.com/b.html”跳转后,b.html能够读取window.name的值为”a”;并且window.name的值长度通常能够到达2M,ie和firefox甚至能够达到32M,这样的存储容量,足够利用起来作跨域的数据传递了。好吧,咱们如今要作的就是当proxy.html拿到a.html发送过来的数据后把这个数据写入window.name中,而后跳转到b.com下面的代理页面,咱们这里假设是bproxy.html。bproxy.html读取到window.name值后,通知给它父页面b.html就简单了。咱们再来看这个过程能够用图大概示意一下:post

  图例中绿色的双向箭头表示能够通讯,橙色的双向箭头表示不能直接通讯。 最后咱们简单看一下双向通讯的实测效果:

  b.html每次加载的时候都先给a.html发一个”链接请求”,让a.html能够找到proxy.html。因此页面第一次加载的时候会产生三个请求:

  每次b.html向a.html发送消息的时候会产生一个请求:

  每次a.html向b.html发送消息的时候会产生两个请求,其中一个是a.com/proxy.html向b.com/bproxy.html跳转产生的,另外一个是b.html从新向a.html发起“链接请求”时产生的:

  最后简单看一下实测的几个测试页面代码:

  代码片断一,a.com/a.html:   

  代码片断二,a.com/proxy.html:

  代码片断三,b.com/b.html:

  代码片断四,b.com/bproxy.html:

  好吧,如今我必须把话锋调转一下了。前面讲的这么多,也只是抛出来一些以前咱们可能会采用的跨域通讯方法,事实上代理页面、url传参数和window.name、甚至还有一些利用url的hash值的跨域传值方法,都能百度到很多相关资料。但它们都逃不开代理页面,也就不可避免的要产生网络请求,而事实上这并非咱们的本意,咱们本来但愿它们可以直接在客户端通讯,避免没必要要的网络请求开销——这些开销,在访问量超大的站点可能会对服务器产生至关大的压力。那么,有没有更完美一点的替代方案呢? 必须给你们推荐postMessage。postMessage 正是为了知足一些合理的、不一样站点之间的内容能在浏览器端进行交互的需求而设计的。利用postMessage API实现跨域通讯很是简单,咱们直接看一下实例的代码:

  代码片断五,A.com/a.html   

  代码片断六,b.com/b.html:

  代码的关键是message事件是一个拥有data(数据)和origin(来源)属性的DOM事件。data属性是发送的实际数据,origin属性是发送来源。Origin属性很关键,有了这个属性,接收方能够轻易的忽略掉来自不可信源的消息,也就能有效避免跨域通讯这个开口给咱们的源安全带来的隐患。接口很强大,因此代码很简单。咱们能够抓包看一下,这个通讯过程彻底是在浏览器端的,没有产生任何的网络请求。同时这个接口目前已经获得了绝大多数浏览器的支持,包括IE8及以上版本,参见下面的图表:

  可是为了覆盖ie6等低版本浏览器,咱们完整的方案里面仍是要包含一下兼容代码,就是最开始介绍的代理页面的方法了,但必须是以postMessage为主,这样即使最后会有某些浏览器由于这种通讯产生一些网络请求,比例也是很是低的了。

http://kb.cnblogs.com/page/198884/

相关文章
相关标签/搜索