postMessage瀏覽器標(biāo)簽頁跨源通信

場景:瀏覽器兩個不同標(biāo)簽頁之間,需要跨域通信。

通常,對于兩個不同頁面的腳本,只有當(dāng)執(zhí)行它們的頁面位于具有相同的協(xié)議(通常為https),端口號(443為https的默認(rèn)值),以及主機 (兩個頁面的模數(shù) Document.domain設(shè)置為相同的值) 時,這兩個腳本才能相互通信。

方案一 url拼接

可以通過url后面拼接相關(guān)參數(shù)的方式進行通信。
不過這個方法有挺多不好之處。
第一:把數(shù)據(jù)參數(shù)都暴露在url里,不夠安全。
第二:數(shù)據(jù)大小受限制。第一點可以通過加密提高安全系數(shù),不過第二點,url的地址是有大小限制的,而且不用的瀏覽器的url大小最大總長度也是有所區(qū)別。當(dāng)需要傳遞的數(shù)據(jù)比較大的時候,這個方法就沒法使用了。

方案二 postMessage

window對象有一個postMessage方法。正確使用時,能安全的實現(xiàn)兩個不用瀏覽器標(biāo)簽頁通信。

window.postMessage(message, targetOrigin, [transfer]);
message
將要發(fā)送到其他 window的數(shù)據(jù)。它將會被結(jié)構(gòu)化克隆算法序列化。這意味著你可以不受什么限制的將數(shù)據(jù)對象安全的傳送給目標(biāo)窗口而無需自己序列化。[1]
targetOrigin
通過窗口的origin屬性來指定哪些窗口能接收到消息事件,其值可以是字符串"*"(表示無限制)或者一個URI。在發(fā)送消息的時候,如果目標(biāo)窗口的協(xié)議、主機地址或端口這三者的任意一項不匹配targetOrigin提供的值,那么消息就不會被發(fā)送;只有三者完全匹配,消息才會被發(fā)送。這個機制用來控制消息可以發(fā)送到哪些窗口;例如,當(dāng)用postMessage傳送密碼時,這個參數(shù)就顯得尤為重要,必須保證它的值與這條包含密碼的信息的預(yù)期接受者的origin屬性完全一致,來防止密碼被惡意的第三方截獲。如果你明確的知道消息應(yīng)該發(fā)送到哪個窗口,那么請始終提供一個有確切值的targetOrigin,而不是*。不提供確切的目標(biāo)將導(dǎo)致數(shù)據(jù)泄露到任何對數(shù)據(jù)感興趣的惡意站點。
transfer 可選
是一串和message 同時傳遞的 Transferable 對象. 這些對象的所有權(quán)將被轉(zhuǎn)移給消息的接收方,而發(fā)送一方將不再保有所有權(quán)。
2.1 源瀏覽器標(biāo)簽窗口傳數(shù)據(jù)給新瀏覽器標(biāo)簽窗口(源=>新)
源瀏覽器窗口標(biāo)簽?zāi)_本
// newWindow 為打開的新窗口對象的引用
let newWindow = window.open(url)
newWindow.postMessage({msg:"hello"}, "*");
新瀏覽器窗口腳本標(biāo)簽
// 監(jiān)聽message
window.addEventListener('message',(e)=>{
   let message = e.data
})
//或者使用
window.onmessage = (e)=>{
   let message = e.data
}
// message為 {msg:"hello"}
2.2 新瀏覽器標(biāo)簽窗口傳數(shù)據(jù)給原瀏覽器標(biāo)簽窗口(新=>源)
源瀏覽器窗口腳本
// 監(jiān)聽message
window.addEventListener('message',(e)=>{
   let message = e.data
})
//或者使用
window.onmessage = (e)=>{
   let message = e.data
}
// 最后message為 {msg:"world"}
新瀏覽器窗口腳本
window.opener.postMessage({msg:"world"},'*')
2.3 結(jié)合使用

現(xiàn)實中打開一個新標(biāo)簽窗口到完成渲染是需要一定的時間的,如果過早使用了postMessage方法,onmessage會無法監(jiān)聽到數(shù)據(jù)。所以這里結(jié)合來使用,在新的標(biāo)
簽窗口渲染完了,通知一下源標(biāo)簽窗口。

源瀏覽器窗口腳本
let newWindow = null
newWindow = window.open(url);

window.onmessage = (e)=>{
  // 當(dāng)接收到新瀏覽器窗口init信息后,才開始發(fā)送消息
  if(e.data.type == 'init'){
    newWindow.postMessage({msg:"hello"}, "*");
  }
}
新瀏覽器窗口腳本
window.onload = (e)=>{
  window.onmessage = (e)=>{
    let message = e.data
  }
  // 防止不是通過window.open創(chuàng)建的
  if(window.opener){
    window.opener.postMessage({type:"init"},"*")
  }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容