這周碰到一個讓人頭疼的需求:要在我的web專案中嵌入另一個第三方web專案。第一時間想到的就是用iframe
了,但問題來了,我和第三方web專案是有互動的,這就違反同源策略了,處理跨域問題是最讓人頭疼的事之一。
需求是這樣的,在我的頁面點選一些按鈕,要實時反饋到iframe
子頁面,子頁面再進行響應。
當時腦子裡第一時間想到的解決方案是:用NGINX
把兩個專案代理到同一域名下。但這樣似乎有點小題大做了,有沒有更方便快捷的方法呢?
在window
物件下有個postMessage
方法,是專門用來解決跨域通訊問題的。
關於postMessage的詳細介紹請戳這裡,不過MDN的文件太詳細了,導致有些同學看完還是一臉懵逼,下面我們就來看看怎麼用postMessage
實現iframe跨域通訊,當你會用了之後再回去看文件,感覺是完全不同的。
首先我們模擬場景,假設有兩個不同源的頁面,iframePage.html
是index.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
獲取到iframe
的window
物件,然後呼叫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
:
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-demo把程式碼clone下來執行試試看。
喜歡本文的朋友可以關注我的微信公眾號,不定期推送一些好文。
本文出自Rockjins Blog,轉載請與作者聯絡。否則將追究法律責任。