摘要:目標當前頁面需要與當前瀏覽器已打開的的某個頁通信,完成某些交互。這種方式性能極其低下,需要通信兩方不停的監聽某項的變化,及其浪費事件隊列處理效率。至此,的消息走通了所有鏈路,成功抵達。
目標
當前頁面需要與當前瀏覽器已打開的的某個tab頁通信,完成某些交互。其中,與當前頁面待通信的tab頁可以是與當前頁面同域(相同的協議、域名和端口),也可以是跨域的。
要實現這個特殊的功能,單單使用HTML5的相關特性是無法完成的,需要有更加巧妙的設計。
暢想現在我們發現下思維,假設多種場景下的解決方案,最終尋找通用解。
case 1兩個需要交互的tab頁面具有依賴關系。
如 A頁面中通過JavaScript的window.open打開B頁面,或者B頁面通過iframe嵌入至A頁面,此種情形最簡單,可以通過HTML5的 window.postMessage API完成通信,由于postMessage函數是綁定在 window 全局對象下,因此通信的頁面中必須有一個頁面(如A頁面)可以獲取另一個頁面(如B頁面)的window對象,這樣才可以完成單向通信;B頁面無需獲取A頁面的window對象,如果需要B頁面對A頁面的通信,只需要在B頁面偵聽message事件,獲取事件中傳遞的source對象,該對象即為A頁面window對象的引用:
B頁面 window.addEventListner("message",(e)=>{ let {data,source,origin} = e; source.postMessage("message echo","/"); });
postMessage的第一個參數為消息實體,它是一個結構化對象,即可以通過“JSON.stringify和JSON.parse”函數還原的對象;第二個參數為消息發送范圍選擇器,設置為“/”意味著只發送消息給同源的頁面,設置為“*”則發送全部頁面。
case 2兩個打開的頁面屬于同源范疇。
若要實現兩個互不相關的通源tab頁面通信,可以使用一種比較巧妙的方式:localstorage。localStorage的存儲遵循同源策略,因此同源的兩個tab頁面可以通過這種共享localStorage的方式實現通信,通過約定localStorage的某一個itemName,基于該key值的內容作為“共享硬盤”方式通信。
不過,如果單純使用localStorage存儲做通信方式會遇到一個問題,就是兩個頁面把握不準通信時機,如果A頁面此刻需要發送給B頁面一條消息“hello B”,它會設置localStorage.setItem("message","hello B"),并且采用setTimeout輪訓等待B的消息;而B此刻也同樣使用setTimeout輪訓等待localStorage的message項的變化,當獲取到"message"字段時,便取出消息"hello B"。B如果要發消息給A,仍然采用同樣套路。
這種方式性能極其低下,需要通信兩方不停的監聽localStorage某項的變化,及其浪費事件隊列處理效率。幸好,HTML5提供了storage事件,通過window對象偵聽storage事件,會偵聽localStorage對象的變化事件(包括item的添加、修改和刪除)。因此,通過事件可以完成高效的通信機制:
A 頁面 window.addEventListener("storage", function(ev){ if (ev.key == "message") { // removeItem同樣觸發storage事件,此時ev.newValue為空 if(!ev.newValue) return; var message = JSON.parse(ev.newValue); console.log(message); } }); function sendMessage(message){ localStorage.setItem("message",JSON.stringify(message)); localStorage.removeItem("message"); } // 發送消息給B頁面 sendMessage("this is message from A");
B 頁面 window.addEventListener("storage", function(ev){ if (ev.key == "message") { // removeItem同樣觸發storage事件,此時ev.newValue為空 if(!ev.newValue) return; var message = JSON.parse(ev.newValue); // 發送消息給A頁面 sendMessage("message echo from B"); } }); function sendMessage(message){ localStorage.setItem("message",JSON.stringify(message)); localStorage.removeItem("message"); }
發送消息采用sendMessage函數,該函數序列化消息,設置為localStorage的message字段值后,刪除該message字段。這樣做的目的是不污染localStorage空間,但是會造成一個無傷大雅的反作用,即觸發兩次storage事件,因此我們在storage事件處理函數中做了if(!ev.newValue) return;判斷。
當我們在A頁面中執行sendMessage函數,其他同源頁面會觸發storage事件,而A頁面卻不會觸發storage事件;而且連續發送兩次相同的消息也只會觸發一次storage事件,如果需要解決這種情況,可以在消息體體內加入時間戳:
sendMessage({ data: "hello world", timestamp: Date.now() }); sendMessage({ data: "hello world", timestamp: Date.now() });
通過這種方式,可以實現同源下的兩個tab頁通信,兼容性
case 3通過caniuse網站查詢storage事件發現,IE的瀏覽器支持非常的不友好,caniuse使用了“completely wrong”的形容詞來表述這一程度。IE10的storage事件會在頁面document文檔對象構建完成后觸發,這在嵌套iframe的頁面中造成諸多問題;IE11的storage Event對象卻不區分oldValue和newValue值,它們始終存儲更新后的值
兩個互不相關的tab頁面通信。
這種情況才是最急需解決的問題,如何實現兩個沒有任何關系的tab頁面通信,這需要一些技巧,而且需要有同時修改這兩個tab頁面的權限,否則根本不可能實現這兩個tab頁的能力。
在上述條件滿足的情況下,我們就可以使用case1 和 case2的技術完成case 3的需求,這需要我們巧妙的結合HTML5 postMessage API 和 storage事件實現這兩個毫無關系的tab頁面的連通。為此,我想到了iframe,通過在這兩個tab頁嵌入同一個iframe頁實現“橋接”,最終完成通信:
tab A -----> iframe A[bridge.html] | | |/ iframe B[bridge.html] -----> tab B
單方向的通信原理如上圖所示,tab A中嵌入iframe A,tab B中嵌入iframe B,這兩個iframe引用相同的頁面“bridge.html”。如果tab A發消息給tab B,首先tab A通過postMessage消息發送給iframe A(tab A可以獲取到iframe A的window對象iframe.contentWindow);此后iframe A通過storage消息完成與iframe B的通信(由于iframeA 與iframe B同源,因此case 2的通信方式這里可以使用);最終,iframe B同樣采用postMessage方式發送消息給tab B(在iframe中通過window.parent引用tab B的window對象)。至此,tab A的消息走通了所有鏈路,成功抵達tab B。
反方向發送消息同樣的道理,這里就不在詳細說明。接下來到了 talk is cheap,show me the code 環節:
tab A: // 向彈出的tab頁面發送消息 window.sendMessageToTab = function(data){ // 由于[#J_bridge]iframe頁面的源文件在vstudio服務器中,因此postMessage發向“同源” document.querySelector("#J_bridge").contentWindow.postMessage(JSON.stringify(data),"/"); }; // 接收來自 [#J_bridge]iframe的tab消息 window.addEventListener("message",function(e){ let {data,source,origin} = e; if(!data) return; try{ let info = JSON.parse(JSON.parse(data)); if(info.type == "BSays"){ console.log("BSay:",info); } }catch(e){ } }); sendMessageToTab({ type: "ASays", data: "hello world, B" })
bridge.html window.addEventListener("storage", function(ev){ if (ev.key == "message") { window.parent.postMessage(ev.newValue,"*"); } }); function message_broadcast(message){ localStorage.setItem("message",JSON.stringify(message)); localStorage.removeItem("message"); } window.addEventListener("message",function(e){ let {data,source,origin} = e; // 接受到父文檔的消息后,廣播給其他的同源頁面 message_broadcast(data); });
tab B window.addEventListener("message",function(e){ let {data,source,origin} = e; if(!data) return; let info = JSON.parse(JSON.parse(data)); if(info.type == "ASays"){ document.querySelector("#J_bridge").contentWindow.postMessage(JSON.stringify({ type: "BSays", data: "hello world echo from B" }),"*"); } }); // tab B主動發送消息給tab A document.querySelector("button").addEventListener("click",function(){ document.querySelector("#J_bridge").contentWindow.postMessage(JSON.stringify({ type: "BSays", data: "I am B" }),"*"); })
至此,通過在tab A和tab B中引入“橋接”功能的iframe[bridge.html]頁面,實現了兩個無關tab頁的雙向通信,這種實現的技巧性較強。
參考資料Communication between tabs or windows
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/91875.html
摘要:目標當前頁面需要與當前瀏覽器已打開的的某個頁通信,完成某些交互。這種方式性能極其低下,需要通信兩方不停的監聽某項的變化,及其浪費事件隊列處理效率。至此,的消息走通了所有鏈路,成功抵達。 目標 當前頁面需要與當前瀏覽器已打開的的某個tab頁通信,完成某些交互。其中,與當前頁面待通信的tab頁可以是與當前頁面同域(相同的協議、域名和端口),也可以是跨域的。 要實現這個特殊的功能,單單使用H...
摘要:關于我的博客掘金專欄路易斯專欄原文鏈接擴展開發定制請求響應頭域本文共字,閱讀需分鐘。那么,我會放棄嗎反向代理顯然不會,既然問題出在上,我去掉就行了。然而無論多少次的學習和模仿,最終的目的還是為了使用,故開發一款定制請求的勢在必行。 本文首發于《程序員》雜志2017年第9、10、11期,下面的版本又經過進一步的修訂。 關于 Github:IHeader 我的博客:louis blog ...
摘要:前端日報精選的現狀網頁性能提升指南精讀現代概覽從入門到真實項目配置瀏覽器的渲染過程與原理動畫動畫中文第期跨瀏覽器頁的通信解決方案嘗試前端面試題及答案總結掘金周刊教程最熟悉的陌生人個人文章技術周刊期知乎專欄的缺口和中的樣式從外 2017-09-18 前端日報 精選 Web 的現狀:網頁性能提升指南精讀《現代 JavaScript 概覽》webpack:從入門到真實項目配置瀏覽器的渲染:過...
摘要:一同源頁面間的跨頁面通信以下各種方式的在線可以戳這里瀏覽器的同源策略在下述的一些跨頁面通信方法中依然存在限制。因此,我們先來看看,在滿足同源策略的情況下,都有哪些技術可以用來實現跨頁面通信。 引言 在瀏覽器中,我們可以同時打開多個Tab頁,每個Tab頁可以粗略理解為一個獨立的運行環境,即使是全局對象也不會在多個Tab間共享。然而有些時候,我們希望能在這些獨立的Tab頁面之間同步頁面的數...
摘要:這是本人寫的第一個擴展,這個擴展的普遍適用性不強,但是確實很方便,具體的開發流程寫在這里,感興趣的看官可以試著自己動手寫一寫這個擴展的作用是,它的適用場景是更換百度搜索頁的關鍵詞,并且跳轉,如果你理解了這個意思,那你一定會想這能有什么用,是 這是本人寫的第一個chrome擴展,這個擴展的普遍適用性不強,但是確實很方便,具體的開發流程寫在這里,感興趣的看官可以試著自己動手寫一寫 這個擴展...
閱讀 623·2023-04-26 01:53
閱讀 2749·2021-11-17 17:00
閱讀 2880·2021-09-04 16:40
閱讀 1983·2021-09-02 15:41
閱讀 830·2019-08-26 11:34
閱讀 1222·2019-08-26 10:16
閱讀 1335·2019-08-23 17:51
閱讀 815·2019-08-23 16:50