iframe 跨域问题
编程    跨域    2016-01-10 23:40:34    978    0    0
  跨域

因为 club 域名没法在阿里云备案(听说有的地方可以备案了@2015),但是我又非常想用我的 club 域名,最后用 iframe 暂时解决了这个问题。

不合适

最直接的方法就是把服务器放在国外,但是这个抽风的网络环境真的很影响心情。

SAE 的服务器相当便宜,所以想用 SAE 做个反代。SAE 如果没有备案的话,也是绕行国外,于是就变成了:访问发送到美国,再从美国发回来,然后请求服务,然后发回美国,然后发回用户。这只会更慢。

所以想到了 SAE 上只放一个 iframe ,iframe 里面来展示网页。这样就没有绕行问题了。

iframe 全屏

只需要关掉 body 的滚动,使用 iframe 的滚动,防止出现双滚动条。然后将 iframe 自己的属性 widthheight都设置为 100%,就可以实现绝大多数网站在绝大多数浏览器中全屏显示。

<body scroll="no">
<iframe id="myframe" src="#" height="100%" width="100%"
        scrolling="auto" frameborder="0">
</iframe>

另外需要去掉 iframe 的边框和 body 的内边距。

    <style>
        body { margin: 0; }
        iframe {border: 0; }
    </style>

iframe 跨域

因为我想实现的是一个完全同步的前端的反代,所以我希望 iframe 中的 titile 和 url 在变化的时候也能同步出来,从而保证外面的页面可以实现刷新功能。不同步 url 的话,如果刷新外面的页面,那么 iframe 里面跳转的 url 就丢失了,这样不好。

但是如果直接操作 iframe 元素来获取 title 和 url ,只适用于不跨域的情况。如果是同一域不同子域,还可通过设置 document.domain,但是像我这种完全跨域的 iframe ,这些方法都不适用。通过这只允许跨域头 CORS/Access-Control-Allow-Origin 也不适用于 iframe ,只针对 XHRs, Fonts, WebGL and canvas.drawImage.

postMessage

所以这个问题只能通过 postMessage 来解决。详细说明

所以我要在我的 外部网页 中添加:

<script type="application/javascript">
    var iframe = document.getElementById('myframe');
    if (iframe.attachEvent) {
                iframe.attachEvent("onload", function(){
        iframe.contentWindow.postMessage("ret={'href':window.location.href,'title':document.title}", '*');
      });
  } else {
        iframe.onload = function(){
             iframe.contentWindow.postMessage("__ret__={'href':window.location.href,'title':document.title}", '*');
        };
    }
</script>

给 iframe 元素发送一个消息,消息可以是任意对象,我偷懒就发送了一个可以被 eval 执行的字符串。

然后在 iframe 中响应事件:

<script type="application/javascript">
    // 跨域通信支持
    window.addEventListener('message', function(event) {
        var index = event.origin.indexOf('/',7);
        if( -1 == index ){
            index = event.origin.indexOf('?');
            if( -1 == index ){
                index = event.origin.length;
            }
        }
        var origin = event.origin.substring(0,index);
        if( origin.endsWith("fullstack.club") ){
            event.source.postMessage(eval(event.data), '*');
        }else{
            alert("who are you?");
            alert(event.origin);
        }
    });
</script>

检查了发送源,所以不是任何人都能给我发的,eval 很危险的。然后 post 回 eval 的执行结果。

外部网页 中再添加响应:

    window.addEventListener('message', function(event) {
        console.log(event.data);
        document.title = event.data.title;
        window.history.pushState(null,event.data.title,turn(event.data.href));
    });

使用了 window.history.pushState 接口,无刷新的同步 url。

文档导航