window.name + postMessage實現不用代理頁的跨域通信


有關跨域通信,可以叫跨域請求,跨域數據訪問,想必大家在工作或多或少地接觸到,網上也能搜出一大羅出來。我的解決方法與他們的不同之處是不使用代理頁。

確切來說,在IE67無法使用postMessage的情況,我們通常需要動態生成一個隱藏iframe來加載通信頁,而它可能是跨域的。window.name的逆天之處在於,iframe.contentWindow.name是共用,即便因為URL的切換導致里面的不斷改變,如果沒有人為修改它,一直就是那個樣子。但不同域的情況下,我們仍然不能訪問iframe中的window.name,這時我們再把iframe切換成本域的頁面就行了。這就是window.name通信的實現機制。

雖說生成一個代理頁沒什么難度,更何況它可以是空白的頁面。但作為一個組件,這也算是一種約束。約束越少越好。經我研究,有兩個URL可以算是本域的永久地址,一個是/favicon.ico(IE下不行),另一個是about:blank。我們就用about:blank作為代理頁!

下面是我的實現:

//by 司徒正美 2010 7 30
                (function() {

                    //數據發送頁的URL,回調,加用於opera的延時時間(可選)
                    var UIloader = function( url, callback, operatime){
                        if(typeof url === "string" && typeof callback == "function"){
                            url += (url.indexOf('?') > 0 ? '&' : '?') + '_time'+ new Date * 1;
                            operatime = typeof operatime === "number" ? operatime  : 3000;
                            var el = document.createElement('iframe'), data;
                            function receive(e){
                                e = e || event;
                                el._state = 2;
                                callback(e.data)
                                if(window.removeEventListener){
                                    window.addEventListener('message', receive, false)
                                }else{
                                    window.detachEvent('onmessage', receive);
                                }
                                body.removeChild(el)
                            }
                            if(window.addEventListener ){
                                window.addEventListener('message', receive, false)
                            }else{
                                window.attachEvent('onmessage', receive);
                            }
                            el.style.display = "none";
                            el._state = 0;
                            var body = document.body || document.documentElement;
                            body.insertBefore( el, body.firstChild );
                            ;(function( node, type, fn ) {
                                if ( window.VBArray ) {
                                    node.attachEvent('on' + type, fn);
                                } else {
                                    node.addEventListener(type, fn, false);
                                }
                            })(el, 'load', function eee() {
                                if(el._state === 1 ) {
                                    try {
                                        data = el.contentWindow.name;
                                    } catch(e) {}
                                    el._state = 2;
                                    callback(data)
                                    callback = function(){}
                                    body.removeChild(el)
                                } else if(el._state === 0) {
                                    setTimeout(function(){
                                        el._state = 1;
                                        el.contentWindow.location.replace("about:blank")
                                    }, (window.opera ? operatime : 31) )//必須等iframe的資源都加載完才跳轉,opera顯然load觸發時機不對
                                }
                            });
                            el.src = url;
                        }else{
                            throw "arguments error"
                        }
                    }

                    UIloader("http://www.cnblogs.com/rubylouvre/archive/2012/07/28/2613565.html",function(a){
                        window.console &&  console.log(a+"!!!!!!!!!!!!!")
                    })
                })();

現場實例觀摩:數據請求頁數據發送頁,具體代碼見頁面源碼!然后我們就會在數據請求頁的控制台看到打印日志了!

再說回來,為什么叫做UIloader呢,因為一般的跨域數據傳送,使用JSONP就夠了,非常輕便!但對於UI組件,比如grid,它通常包括一個體積也夠為嚇人的JS文件,還有一個樣式表,如果不想通過字符串拼接來渲染界面,我們還可能用到前端模板,當然還有圖片什么的。撇開圖片不談,JS,前端模板,樣式我們都可以統統整到一個JS文件里面的,但會顯然很亂,尤其是大段的CSS樣式,HTML字段,這是不是用HTML來放置它們比較好呢。我們用一個HTML來放置它們,樣式表寫到style標簽中,HTML寫到一個DIV上,腳本寫到一個script中。那么它們它們拼成一個對象:

var data = {
   html: div.innerHTML,
   js: script.text,
   css: style.cssText
}

  window.name = data
    
   if(window.postMessage){
      window.parent.postMessage(data, "*");
   } 

不過需要注意的是postMessage在IE下只能傳字符串,我們只好在發送頁統一返回字符串,用JSON.stringify轉換一下就行了,取回來再用JSON.parse變成對象!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM