一種通用的iframe跨域通訊方法

來源:互聯網
上載者:User

同源策略簡介
如果兩個頁面的協議、連接埠(如果指明了的話)和主機名稱都相同則兩個頁面擁有相同的源。同源策略阻止從一個源載入的文檔或指令碼擷取或設定另一個源載入的文檔的屬性。這個策略可以追溯到 Netscape Navigator 2.0。

但很多時候兩個不同域的頁面之間需要進行通訊,這就產生了跨域通訊的問題了,關於跨域的文章非常多,可以參考10種方式實現跨域資源的共用,下面和大家分享一下我們項目中是如何進行雙向跨域通訊的,工大家參考。

解決方案
對於雙向跨域通訊來說,選擇的方案也不是很多,綜合各種因素,我們選擇FIM + window.postMessage的組合方式來解決跨域通訊問題。因為這兩種方式各有優缺點,window.postMessage,使用非常簡單,但是由於是個比較新的方法,IE6和IE7等老式瀏覽器不支援。FIM方法支援所有的瀏覽器,但比較繁瑣,而且容易產生瀏覽記錄,因此如果瀏覽器支援window.postMessage就採用window.postMessage,否則就採用FIM技術。

window.postMessage解決方案
window.postMessage是HTML5定義的一個很新的方法,這個方法可以很方便地跨window通訊。由於它是一個很新的方法,所以在很舊和比較舊的瀏覽器中都無法使用,比如IE6和IE7,IE8已經支援這個方法了。

window.postMessage的使用方法比較簡單,只有兩個參數,第一個參數是要傳輸的訊息,第二個參數是接收訊息的域,可以用“×”來表示所有的域。發送訊息的代碼如下:

  var o = document.getElementsByTagName('iframe')[0];
  o.contentWindow.postMessage('Hello world', 'http://b.example.org/');
接收訊息頁面的代碼如下:

  window.addEventListener('message', receiver, false);
  function receiver(e) {
    if (e.origin == 'http://example.com') {
      if (e.data == 'Hello world') {
        e.source.postMessage('Hello', e.origin);
      } else {
        alert(e.data);
      }
    }
  }
注意這裡的綁定事件方式只針對非IE瀏覽器,IE瀏覽器需要用attachEvent方式來綁定事件。在上面的代碼裡你可以決定是否要判斷訊息的來源,這裡是從安全形度考慮,防止不安全的訊息。

FIM (Fragment Identitier Messaging)
FIM (Fragment Identitier Messaging)的原理是基於父視窗可以對iframe進行URL讀寫,iframe也可以寫父視窗的URL(注意,跨域時iframe是不可以讀取父視窗的URL的,但可以修改父視窗的URL),URL有一部分被稱 為frag,就是#號及其後面的字元,它一般用於瀏覽器錨點定位,Server端並不關心這部分,應該說HTTP請求過程中不會攜帶frag,所以這部分的修改不會產生HTTP請求,也就是頁面不會重新整理,但是會產生瀏覽器記錄。FIM的原理就是改變URL的frag部分來進行雙向通訊。每個window通過改變其它window的location來發送訊息,並通過監聽自己的URL的變化來接收訊息。這個方式的通訊會造成一些不必要的瀏覽器記錄,而且有些瀏覽器 不支援onhashchange事件,需要輪詢來獲知URL的改變,最後,URL在瀏覽器下有長度限制,這個制約了每次傳送的資料量。

先說父視窗向iframe發送訊息,代碼如下:
  var o = document.getElementById('iframe');
  o.href = 'http://www.weakweb.com#name=boris';
這樣我們就把值傳到iframe裡面了,下面就需要考慮iframe如何監聽何時有訊息傳遞過來。這裡有兩個方法:1,在iframe裡使用setInterval()輪詢來判斷當前的iframe的URL是否發生了變化,缺點是輪詢會產生效能消耗;2,在父視窗裡改變iframe的大小來觸發iframe的window.onresize事件,缺點是要改變iframe視窗的大小。當iframe發現有訊息傳送過來時就可以通過locaiton.hash來讀取了。

iframe向父視窗發送訊息
  parent.location.href = 'http://www.company.com#height=100';
上面提過,跨域時iframe可以寫父視窗的URL,但是不可以讀取的,所以這裡我們就通過frag的方式把訊息傳遞到父視窗了,同樣,父視窗也需要監聽啥時候有訊息傳進來,這裡只能用輪詢的方式了。

對FIM的一點改進
當父視窗採用修改iframe視窗大小的方式告訴iframe有訊息傳遞方式時,這種方式有一個非常明顯的缺點,那就是iframe視窗的大小改變會嚴重影響使用者體驗。因此這裡採用的代理機制來處理。

我們額外建立了代理iframe叫proxyIframe吧,proxyIframe和之前的contentIframe是同一個域的,同時proxyIframe是隱藏不可見的,這樣我們先把訊息傳遞到proxyIframe裡,然後在由proxyIframe發送到contentIframe,因為這兩個iframe是同域的,所以可以很方便的傳遞。這樣做的好處是我們只修改proxyIframe的大小而不用修改contentIframe的大小,這樣不會影響使用者體驗,缺點是比之前複雜了一些。

瀏覽器的URL長度是有限制的,尤其是IE,所以我們可以通過分段傳輸來解決這個問題。

範例程式碼
我寫了一個測試代碼,其中包含了三個頁面,一個父頁面和兩個iframe頁面,點擊下載

一個如何在本地測試回合上面的範例程式碼的簡單方法,在C:\Windows\System32\drivers\etc\hosts檔案,在裡面添加兩個網域名稱指向127.0.0.1,這樣就可以測試跨域了。我的hosts檔案如下:

   127.0.0.1       a.com
    127.0.0.1       b.com
 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.