您當前的位置:首頁 > 攝影

面試裝X:我知道的前端跨頁面通訊

作者:由 拜小白 發表于 攝影時間:2022-01-17

​背景

在實際的業務中,會遇到這樣一個問題,使用者開啟多個 Tab 頁面A、B、C、D、E。。。。當用戶在 E Tab 頁退出登入,並且登入到新的賬號,然後使用者切換到非 E 的 Tab 時,發現登入資訊沒有重新整理, 並且由於登入資訊沒有重新整理,會出現操作異常。這個問題簡單來說就是多個 Tab 資訊沒有同步。問題的關鍵在於一個 Tab 退出重新登入,需要通知到其他的 Tab 重新整理到最新的資訊。本質問題就是

解決前端跨頁面通訊

本篇文章就是對前端跨頁面通訊的解決方案做了一個瞭解。

onstorage

WindowEventHandlers。onstorage 屬性包含一個在 storage 事件觸發時執行的事件處理程式。當更改儲存時會觸發事件處理程式。

語法

window。onstorage = function(){。。。};

window。onstorage = function(e) {

console。log(`The ${e。key} key has been changed from ${e。oldValue} to ${e。newValue} 。`);

};

面試裝X:我知道的前端跨頁面通訊

面試裝X:我知道的前端跨頁面通訊

Tips

該事件不在導致資料變化的當前頁面觸發(如果瀏覽器同時開啟一個域名下面的多個頁面,當其中的一個頁面改變 資料時,其他所有頁面的 storage 事件會被觸發,而原始頁面並不觸發 storage 事件)。

sessionStorage(❎)不能觸發 storage 事件 , localStorage(✅)可以。

如果修改的值未發生改變,將不會觸發 onstorage 事件。

優點:瀏覽器支援效果好、API直觀、操作簡單。缺點:部分瀏覽器隱身模式下,無法設定 localStorage。如safari,這樣也就導致 onstrage 事件無法使用。

面試裝X:我知道的前端跨頁面通訊

除開少數情況,localStorage的相容性不錯,就當前國內的情況,已經基本沒有問題了。localStorage 的原理很簡單,瀏覽器為每個域名劃出一塊本地儲存空間,使用者網頁可以透過 localStorage 名稱空間進行讀寫。

BroadCast Channel

BroadcastChannel 介面代理了一個命名頻道,

可以讓指定 origin 下的任意 browsing context 來訂閱它。它允許同源的不同瀏覽器視窗,Tab頁,frame或者 iframe 下的不同文件之間相互通訊

。透過觸發一個 message 事件,訊息可以廣播到所有監聽了該頻道的 BroadcastChannel 物件。

說到 BroadCast Channel 不得不說一下 postMessage,他們二者的最大區別就在於 postMessage 更像是點對點的通訊,而 BroadCast Channel 是廣播的方式,點到面。

語法

// 建立

const broadcastChannel = new BroadcastChannel(‘channelName’);

// 監聽訊息

broadcastChannel。onmessage = function(e) {

console。log(‘監聽訊息:’, e。data);

};

// 傳送訊息

broadcastChannel。postMessage(‘測試:傳送訊息’);

// 關閉

broadcastChannel。close();

面試裝X:我知道的前端跨頁面通訊

面試裝X:我知道的前端跨頁面通訊

Tips

監聽訊息除了 。onmessage 這種方式,還可以 使用addEventListener來新增‘message’監聽,

關閉除了使用 Broadcast Channel 例項為我們提供的 close 方法來關閉 Broadcast Channel。我們還可取消或者修改相應的‘message’事件監聽。兩者是有區別的:取消‘message’監聽只是讓頁面不對廣播訊息進行響應,Broadcast Channel 仍然存在;而呼叫 close 方法會切斷與 Broadcast Channel 的連線,瀏覽器才能夠嘗試回收該物件,因為此時瀏覽器才會知道使用者已經不需要使用廣播頻道了。

相容性:如果不使用 IE 和 sf on iOS 瀏覽器,相容性還是可以的。

面試裝X:我知道的前端跨頁面通訊

Service Worker

Service Worker 是一個可以長期執行在後臺的 Worker,能夠實現與頁面的雙向通訊。多頁面共享間的 Service Worker 可以共享,將 Service Worker 作為訊息的處理中心(中央站)即可實現廣播效果。

語法

/* 監聽安裝事件,install 事件一般是被用來設定你的瀏覽器的離線快取邏輯 */

this。addEventListener(‘install’, function (event) {

/* 透過這個方法可以防止快取未完成,就關閉serviceWorker */

event。waitUntil(

/* 建立一個名叫V1的快取版本 */

caches。open(‘v1’)。then(function (cache) {

/* 指定要快取的內容,地址為相對於跟域名的訪問路徑 */

return cache。addAll([‘。/index。html’]);

})

);

});

/* 註冊fetch事件,攔截全站的請求 */

this。addEventListener(‘fetch’, function (event) {

event。respondWith(

// magic goes here

/* 在快取中匹配對應請求資源直接返回 */

caches。match(event。request)

);

});

/* 監聽訊息,通知其他 Tab 頁面 */

this。addEventListener(‘message’, function(event) {

this。clients。matchAll()。then(function(clients) {

clients。forEach(function(client) {

// 這裡的判斷目的是過濾掉當前 Tab 頁面,也可以使用 visibilityState 的狀態來判斷

if(!client。focused) {

client。postMessage(event。data)

}

})

})

})

111

面試裝X:我知道的前端跨頁面通訊

面試裝X:我知道的前端跨頁面通訊

Tips

Service workers 本質上充當 Web 應用程式、瀏覽器與網路(可用時)之間的代理伺服器。所以本質上來說 Service Worker 並不自動具備“廣播通訊”的功能,需要改造 Service Worker 新增些程式碼,將其改造成訊息中轉站。在 Service Worker 中監聽了message事件,獲取頁面傳送的資訊。然後透過 self。clients。matchAll() 獲取當前註冊了 Service Worker 的所有頁面,透過呼叫每個的 postMessage 方法,向頁面傳送訊息。這樣就把從一處(某個Tab頁面)收到的訊息通知給了其他頁面。

相容性:IE 全軍覆沒,其他瀏覽器還行,整體來說一般。

面試裝X:我知道的前端跨頁面通訊

open & opener

當我們 系統中透過 window。open 開啟一個新頁面時,window。open 方法會返回一個被開啟頁面的引用,而被開啟頁面則可以透過 window。opener 獲取到開啟它的頁面的引用(當然這是在沒有指定noopener的情況下)。

番外關於 noopener

Google

我們在系統中經常會這樣使用 a 標籤跳轉到第三方網站,有時,當您單擊網站上的連結時,該連結將在新選項卡中開啟,但舊選項卡也會被重定向到其他網路釣魚網站,它會要求您登入或開始將一些惡意軟體下載到您的裝置。這樣存在一定的安全隱患,此時在新開啟的頁面中可透過 window。opener 獲取到源頁面的 window 物件, 這就埋下了安全隱患。比如:

你自己的網站 A,點選如上鍊接打開了第三方網站 B。

此時網站 B 可以透過 window。opener 獲取到 A 網站的 window 物件。

然後透過 window。opener。location。href = ‘www。baidu。com’ 這種形式跳轉到一個釣魚網站,洩露使用者資訊。

為了避免這樣的問題,可以新增引入了 rel=“noopener” 屬性, 這樣新開啟的頁面便獲取不到來源頁面的 window 物件了, 此時 window。opener 的值是 null。

Google

但是由於一些老的瀏覽器並不支援 noopener ,通常 noopener 和 noreferrer 會同時設定, rel=“noopener noreferrer”。

語法

回到主題,使用 window。opener 如何實現跨頁面通訊了。

收集物件

// 收集 window 物件:單個開啟頁面

const windowOpen = window。open(‘xxx’);

// 收集 window 物件:多個開啟頁面,開啟一個頁面就需要將開啟的 window 物件收集起來,以便於釋出廣播

const windowOpens = [];

const windowOpen = window。open(‘xxx’);

windowOpens。push(windowOpen);

傳送訊息

// 傳送訊息:單個頁面

windowOpen。postMessage(data);

// 傳送訊息:多個頁面

windowOpens。forEach((window) => window。postMessage(data));

接受訊息,對於接受訊息來說,可能只是接受訊息,但是可能接受訊息的頁面也打開了頁面,這種情況需要將訊息繼續傳遞下去

window。addEventListener(‘message’, function (e) {

const data = e。data;

console。log(data);

windowOpens。forEach((window) => window。postMessage(data));

});

Tips

在收集到的 window 物件中,可能有的 Tab 視窗被關閉了,這種情況下的 Tab 不需要進行訊息傳遞。

對於接受訊息的一方來說,需要繼續傳遞訊息,但是這裡存在一個問題就是訊息回傳,可能出現兩者之間訊息的死迴圈傳遞。

這種方式,類似擊鼓傳花,一個傳一個,傳遞的訊息從前往後,一條鎖鏈。

但是如果頁面不是透過一個頁面開啟的,而且直接開啟的,或者從三方網站跳轉的,那這條鎖鏈將斷開。

所以這種方式基本只做瞭解,問題太多,可不做參考。

完善的程式碼如下:

#FormatImgID_19##FormatImgID_20#

SharedWorker

SharedWorker 介面代表一種特定型別的 worker,可以從幾個瀏覽上下文中訪問,例如幾個視窗、iframe 或其他 worker。它們實現一個不同於普通 worker 的介面,具有不同的全域性作用域, SharedWorkerGlobalScope。

語法

// 建立共享執行緒物件

let worker = new SharedWorker(“。/sharedWorker。js”);

// 手動啟動埠

worker。port。start();

// 處理從 worker 返回的訊息

worker。port。onmessage = function (val) {

。。。

};

還沒有人點贊

// 。/sharedWorker。js

let a = 666;

console。log(‘shared-worker’);

onconnect = function (e) {

const port = e。ports[0];

console。log(‘shared-worker connect’);

// 不能使用這種方式監聽事件

// port。addEventListener(‘message’, () => {

// port。postMessage(++a);

// });

port。postMessage(a);

port。onmessage = () => {

port。postMessage(++a);

};

console。log(‘當前點贊次數:’, a);

};

面試裝X:我知道的前端跨頁面通訊

Tips

如果要使 SharedWorker 連線到多個不同的頁面,這些頁面必須是同源的(相同的協議、host 以及埠)。

Shared Worker 在實現跨頁面通訊時的,它無法主動通知所有頁面,需要重新整理頁面或者是定時任務來檢查是否有新的訊息。在例子中我是手動重新整理的,當然可以使用 setInterval 來定時重新整理。

如果需要除錯 SharedWorker,使用

chrome://inspect/#workers

面試裝X:我知道的前端跨頁面通訊

面試裝X:我知道的前端跨頁面通訊

sharedWorker。js 不能使用 。addEventListener 來監聽 message 事件,監聽無效。

相容性一般。

面試裝X:我知道的前端跨頁面通訊

總結

在上面列舉了五種前端跨頁面通訊的方式,當然對前端來說遠遠不止這五種方式,還有其他方案例如:使用 hashchange、cookie、Websocket、postMessage 都是可以的。文章中只是列舉了部分。並且文章中的方案都是針對同源的 Tab。

文章的前三種解決方式不論是 Broadcast Channel,還是 Service Worker ,或是 storage 事件,其都是“廣播模式”:一個頁面將訊息通知給一個“中央站”,再由“中央站”通知給各個頁面。

而對於 open & opener 這種方式,類似擊鼓傳花,一個傳一個,傳遞的訊息從前往後,一條鎖鏈。但是如果頁面不是透過一個頁面開啟的,而且直接開啟的,或者從三方網站跳轉的,那這條鎖鏈將斷開。

Shared Worker 的最大問題在於實現跨頁面通訊時的,它無法主動通知所有頁面,需要重新整理頁面或者是定時任務來檢查是否有新的訊息,也就是需要配合輪詢來使用。

最終在我們團隊對於前端跨頁面通訊最後選擇的解決方案是使用 onstorage,主要考量的三個方面:

相容性。瀏覽器支援度。

通用性。能否覆蓋需求、是否具有拓展性。

便捷性。開發便捷程度。

其他方案在這三個方面來說都或多或少存在一些美中不足。

參考

https://www。

w3cschool。cn/fetch_api/

fetch_api-xsc32qgb。html

https://

developer。mozilla。org/z

h-CN/docs/Web/API/WindowEventHandlers/onstorage#compat-mobile

https://

juejin。cn/post/68449038

11232825357#heading-4

https://

zhuanlan。zhihu。com/p/81

237384

https://

developer。mozilla。org/e

n-US/docs/Web/API/BroadcastChannel

https://

juejin。cn/post/68449038

11228663815

https://

developer。mozilla。org/z

h-CN/docs/Web/API/Service_Worker_API

https://

developer。mozilla。org/e

n-US/docs/Web/API/SharedWorker

https://

zhuanlan。zhihu。com/p/36

6736912

https://

blog。bhanuteja。dev/noop

ener-noreferrer-and-nofollow-when-to-use-them-how-can-these-prevent-phishing-attacks

https://

blog。csdn。net/huangpb12

3/article/details/89498418

https://

my。oschina。net/ahaoboy/

blog/4321769

標簽: window  頁面  function  data  訊息