[轉]iframe跨域通訊的通用解決方案

2個信使的狀況

1、背景 html

在這個Web頁面愈來愈豐富的時代,頁面經過iframe嵌入其餘的頁面也愈來愈常見。但因爲瀏覽器同源策略的限制,不一樣域之間屬性和操做是沒法直接交互的,因此在這個時候,開發者多多少少須要一些方案來突破這些限制。跨域問題涉及的地方也不少,如文檔之間的消息通訊、ajax請求、Cookie等,本文討論的是iframe和父頁面的消息通訊。 git

2、現狀 github

目前網上也能夠找到各類解決方案(少說都有10+個,有興趣的話能夠去看看),對於現代瀏覽器來講,原生的postMessage API必定是不二的選擇,因此各類方案的不一樣點均在於IE 六、7中的處理(不用兼容IE六、7的同志能夠去看其餘文章了)。固然這麼多方案有各類優缺點,甚至有些只支持單向跨域,我的以爲實際意義不大。另一些方案須要proxy.html這樣的代理頁面作中轉,可是涉及服務器上的部署,而且對於多方合做來講仍是有些麻煩。 web

3、思路 ajax

雖然再也不復述現有的各類方案,但仍是想交待一點上下文。相信網上看到最多方案就是利用location.hash或是window.name進行iframe的跨域通訊: 跨域

  • location.hash會直接暴露在URL裏,而且在一些瀏覽器裏會產生歷史記錄,數據安全性不高也影響用戶體驗,因此不作考慮。另外因爲URL大小的限制,支持傳遞的數據量也不大。
  • window.name相比來說就好不少了,支持2M的數據量,而且當iframe的頁面跳到其餘地址時,其window.name值保持不變,反作用能夠說是最小的。

講到這思路也比較清晰了,我們就用window.name唄,但問題又來了:只有兩個頁面同域時才能訪問window.name。這個問題還好,只要把iframe改成與父頁面同域就能夠了。這又衍生了新的問題:這不是意味着只能單向通訊了嗎,iframe怎麼向父頁面發消息(不可能去改父頁面的location吧)?在暗罵坑爹的同時偶然發現了一個很神奇的方法,就是想訪問一個iframe的window.name時,只要將其location改成‘about:blank’便可,屢試不爽~沒錯這個「特性」能夠視爲IE6/7的一項安全性問題,但利用這個特性來進行跨域通訊並無實際的安全風險。 瀏覽器

具體的實現見下圖,在iframe的內部再建立一個iframe(咱們稱之爲信使),父子頁面輪詢信使的window.name,父子頁面各自使用變量保存window.name,輪詢時發現有變化即被視爲收到消息。基本原理就是這麼簡單,咱們繼續.. 安全

1個信使的狀況

圖1 服務器

做爲一個通用的解決方案,咱們的目標是提供一個js文件,封裝通訊的接口,須要通訊的頁面只要加載js文件就行。但在封裝前,須要考慮更復雜一點的狀況:當父子頁面雙方頻率較高地雙向通訊時,如何進行支持?按照上述的方案,信使的window.name並無讀寫鎖的概念,這意味着消息很容易亂掉或被漏掉。因此更好的方案應該是:建立兩個信使,分別負責」父–>子」和」子–>父」的消息傳遞,而且爲了防止消息被沖掉,發送消息時會維護一個消息隊列,在取消息時處理消息隊列裏的全部消息。見圖2。 dom

2個信使的狀況

圖2

4、封裝

最後的封裝就是加入了postMessage API的檢測,另外也要判斷是否爲跨域,這樣就知足了全部iframe通訊的狀況了。這裏實現的信使只負責消息的監聽和發送,因此在使用上是很是簡單的:

JavaScript

// 父頁面中
// 初始化信使, 告知與其交互的iframe引用
var messenger = Messenger.initInParent(iframeEl);
 
// 監聽消息
messenger.onmessage = function (data) {
          ...
};
 
// 發送消息
messenger.send(message);
// iframe中
// 初始化信使
var messenger = Messenger.initInIframe();
 
// 監聽消息
messenger.onmessage = function (data) {
      ...
};
 
// 發送消息
messenger.send(message);



具體使用能夠參考下方的demo : )

5、總結

雖然國內也有人提過使用」about:blank」進行iframe通訊的,可是代碼的封裝和可讀性都不是太好,本方案是一日本人所提出,我以爲處理的很好,因此就拿出來和你們分享下。雖然嘗試過優化輪詢那一塊,但暫時無果,有興趣的朋友能夠一塊兒研究下~

DEMO:點擊這裏

腳本下載:http://biqing.alloyteam.com/lab/messenger/messenger.js

GitHub:https://github.com/biqing/MessengerJS

相關文章
相關標籤/搜索