使用 postMessage 解決 iframe 跨域通訊問題

FREAKFILTH發表於2017-05-05

這周碰到一個讓人頭疼的需求:要在我的web專案中嵌入另一個第三方web專案。第一時間想到的就是用iframe了,但問題來了,我和第三方web專案是有互動的,這就違反同源策略了,處理跨域問題是最讓人頭疼的事之一。

需求是這樣的,在我的頁面點選一些按鈕,要實時反饋到iframe子頁面,子頁面再進行響應。

當時腦子裡第一時間想到的解決方案是:用NGINX把兩個專案代理到同一域名下。但這樣似乎有點小題大做了,有沒有更方便快捷的方法呢?

window物件下有個postMessage方法,是專門用來解決跨域通訊問題的。

關於postMessage的詳細介紹請戳這裡,不過MDN的文件太詳細了,導致有些同學看完還是一臉懵逼,下面我們就來看看怎麼用postMessage實現iframe跨域通訊,當你會用了之後再回去看文件,感覺是完全不同的。

首先我們模擬場景,假設有兩個不同源的頁面,iframePage.htmlindex.html的子頁面:

<!-- index.html -->

<body style="border:5px solid #333;">

  <h1>this is index</h1>

  <iframe src="./iframePage.html" id='myframe'></iframe>

</body>複製程式碼
<!-- iframePage -->

<body style="border:5px solid #333;">

  <h1>this is iframePage</h1>

</body>複製程式碼

現在這兩個iframe是無法通訊,因為它們是不同源的(假設存在跨域問題),這時候就要用到postMessage了。

我們先試著從父頁面向子頁面傳送一條訊息:

// idnex.html

//獲取iframe元素
iFrame = document.getElementById('myframe')

//iframe載入完畢後再傳送訊息,否則子頁面接收不到message
iFrame.onload = function(){

  //iframe載入完立即傳送一條訊息
  iFrame.contentWindow.postMessage('MessageFromIndex1','*');

}複製程式碼

我們知道postMessage是掛載在window物件上的,所以等iframe載入完畢後,用iFrame.contentWindow獲取到iframewindow物件,然後呼叫postMessage方法,相當於給子頁面傳送了一條訊息。

postMessage方法第一個引數是要傳送的資料,可以是任何原始型別的資料。

Gecko 6.0 (Firefox 6.0 / Thunderbird 6.0 / SeaMonkey 2.3)之前,第一個引數必須是一個字串。

postMessage方法第二個引數可以設定要傳送到哪個url,如果當前子頁面的url和設定的不一致,則會傳送失敗,我們設定為*,代表所有url都允許傳送。

postMessage方法還有第三個引數,屬於高階用法,這裡不做討論,可以稍後去MDN瞭解。

訊息傳送到iframePage.html,我們來接收message:

// iframePage.html

//回撥函式
function receiveMessageFromIndex ( event ) {
  console.log( 'receiveMessageFromIndex', event )
}

//監聽message事件
window.addEventListener("message", receiveMessageFromIndex, false);複製程式碼

我們只需要在子頁面監聽message事件,並且設定好回撥函式即可,來看看列印出來的event

使用 postMessage 解決 iframe 跨域通訊問題

event物件中的data屬性存放著我們從父頁面傳過來的資料,就這麼簡單!

讓我們再試試從子頁面傳送資料給父頁面:

// iframePage.html

//給父頁面傳送訊息,data為物件
parent.postMessage( {msg: 'MessageFromIframePage'}, '*');複製程式碼

父頁面接收資料:

//index.html

//回撥函式
function receiveMessageFromIframePage (event) {
    console.log('receiveMessageFromIframePage', event)
}

//監聽message事件
window.addEventListener("message", receiveMessageFromIframePage, false);複製程式碼

我看看到,的確可以傳輸不同的資料,此時data為一個物件:

使用 postMessage 解決 iframe 跨域通訊問題

大家可以到postMessage-demo把程式碼clone下來執行試試看。

喜歡本文的朋友可以關注我的微信公眾號,不定期推送一些好文。

使用 postMessage 解決 iframe 跨域通訊問題

本文出自Rockjins Blog,轉載請與作者聯絡。否則將追究法律責任。

相關文章