🧑💻 寫在開頭
點贊 + 收藏 === 學會🤣🤣🤣
如何跨越不同窗口通信
在現代 Web 開發中,多個窗口或標籤頁之間的通信成為了越來越常見的需求,尤其是在應用需要同步數據、共享狀態或進行實時更新的場景中。不同窗口間的通信方式有很多種,選擇合適的方式可以大大提高開發效率和用户體驗。本文將詳細介紹幾種常見的瀏覽器多窗口通信技術,並結合實際代碼示例,分析它們的優缺點及兼容性。
1、window.postMessage的使用實踐
window.postMessage 是一種用於跨域或同域窗口間安全通信的 API。通過 postMessage,我們可以輕鬆實現父子窗口、同域不同標籤頁或跨域之間的數據傳遞。
以下為實際運用中常用的2種用法:
1.1 iframe使用獲得窗口引用
通過 window.postMessage,父頁面可以與嵌套的 iframe 進行通信。關鍵是要正確獲得 iframe 的引用,然後通過該引用發送消息
父頁面 parent.html
<button onclick="send()">向iframe窗口發送消息</button>
<iframe id="myIframe" src="http://localhost:5500/child.html"></iframe>
<script>
function send() {
const iframe = document.getElementById('myIframe');
// 獲得iframe窗口得window,向 iframe 發送消息
iframe.contentWindow.postMessage('Hello from Parent', '*');
}
// 監聽來自 iframe 的消息
window.addEventListener('message', (event) => {
if (event.origin === 'http://localhost:5500' && typeof event.data === "string") {
console.log('來自 iframe 的消息:', event.data);
}
});
</script>
iframe 頁面 child.html
<button onclick="send()">向父窗口發送消息</button>
<script>
function send() {
// 向父頁面發送消息
window.parent.postMessage("Hello from Child", "*");
}
// 監聽來自父頁面的消息
window.addEventListener("message", (event) => {
console.log("來自父頁面的消息:", event.data);
});
</script>
注意:本地直接打開文件為file:// 協議,不能使用,瀏覽器會將 file:// 協議視為一個特殊的源,因為安全策略會無法使用
解決辦法:將你的代碼部署在一個本地開發服務器上(如使用 http-server、Live Server 或其他簡單的 HTTP 服務器)。這樣,你就可以通過 http://localhost/ 進行通信
1.2 window.open使用,獲取窗口引用
通過 window.open 打開一個新的窗口或標籤頁時,我們可以獲得新窗口的引用,並通過 postMessage 實現通信。
父頁面 parent.html
<button onclick="openWin()">打開新窗口</button>
<button onclick="send()">發送消息</button>
<script>
let win = null
function openWin() {
win = window.open('http://localhost:5500/child.html', '_blank');
}
function send() {
// 向新窗口發送消息
win && win.postMessage('Hello from Parent', '*');
}
// 監聽來自 child 窗口的消息
window.addEventListener('message', (event) => {
if (event.origin === 'http://localhost:5500' && typeof event.data === "string") {
window.alert('來自 child 窗口 的消息:' + event.data);
}
});
子頁面child.html
<button onclick="send()">向父窗口發送消息</button>
<script>
function send() {
// 向父頁面發送消息
window.opener.postMessage('Hello from Child', '*');
}
// 監聽來自父頁面的消息
window.addEventListener("message", (event) => {
if (event.origin === 'http://localhost:5500' && typeof event.data === "string") {
window.alert("來自父頁面的消息:" + event.data);
}
});
</script>
兼容性:
window.open和postMessage在大多數現代瀏覽器中得到支持。需要注意:如果打開的窗口被瀏覽器阻止彈出,通信無法進行。
2、客户端存儲 + 定時器實時刷新監聽【不推薦】
通過客户端存儲(如 cookie、localStorage 或 sessionStorage)結合定時器(如 setInterval 或 requestAnimationFrame),可以實現同源域名下不同窗口之間的通信。此方式利用定時器定時檢查存儲值的變化,但缺點是浪費性能資源,且數據更新不夠實時
父頁面 parent.html
// 父頁面向 storage 寫入數據
localStorage.setItem('message', 'Hello from Page');
// 子頁面 定時檢查 localStorage 變化
setInterval(() => {
const message = localStorage.getItem('message');
if (message) {
console.log('Received message:', message);
}
}, 1000);
3、StorageEvent 事件監聽
當一個頁面修改了 localStorage 或 sessionStorage,其他同源的頁面可以通過監聽 storage 事件來獲取更新。此方法適合同一域下多個頁面之間的通信。
父頁面 parent.html
<button onclick="send()">向子窗口發送消息</button>
<script>
let i = 1
function send() {
// 向新窗口發送消息
localStorage.setItem('child-message', 'Hello from Parent' + i++);
}
window.addEventListener('storage', (event) => {
if (event.key === 'parent-message') {
window.alert(event.newValue);
}
});
</script>
子頁面child.html
<button onclick="send()">向父窗口發送消息</button>
<script>
let i = 1
function send() {
// 向父頁面發送消息
localStorage.setItem('parent-message', 'Hello from Child' + i++);
}
// 監聽來自父頁面的消息
window.addEventListener('storage', (event) => {
if (event.key === 'child-message') {
window.alert(event.newValue);
}
});
</script>
兼容性:
StorageEvent事件在大多數現代瀏覽器中都得到了支持,包括 Chrome、Firefox、Safari 和 Edge。它僅在同源的多個頁面之間有效,無法跨域使用。
4、Broadcast Channel
BroadcastChannel API 允許同源的多個窗口、標籤頁、iframe 或 Web Worker 之間進行消息廣播。這種方法非常適合同一應用中多個窗口或標籤頁之間的實時通信。
父頁面 parent.html
<button onclick="send()">向子窗口發送消息</button>
<script>
function send() {
// 發送消息到廣播頻道
const channel = new BroadcastChannel('parnt-message');
channel.postMessage('Hello from Parent');
}
// 監聽廣播頻道的消息
const channel = new BroadcastChannel('child-message');
channel.addEventListener('message', (event) => {
window.alert(event.data)
});
子頁面child.html
<button onclick="send()">向父窗口發送消息</button>
<script>
function send() {
// 發送消息到廣播頻道
const channel = new BroadcastChannel('child-message');
channel.postMessage('Hello from Child');
}
// 監聽廣播頻道的消息
const channel = new BroadcastChannel('parnt-message');
channel.addEventListener('message', (event) => {
window.alert(event.data)
});
兼容性:
BroadcastChannel在 Chrome、Firefox 和 Edge 中得到支持,但 Safari 目前不支持,且不適用於 IE 或較舊的瀏覽器版本。
5、SharedWorker
SharedWorker 是一種非常強大的技術,允許多個窗口、標籤頁或 iframe 共享同一個後台線程。它適用於跨窗口的實時數據共享和複雜計算任務。
如果要使 SharedWorker 連接到多個不同的頁面,這些頁面必須是同源的(相同的協議、host 以及端口)。
worker.js:執行指定 url 腳本的共享 web worker
// 共享線程worker.js
const ports = [];
self.onconnect = function (event) {
const port = event.ports[0];
ports.push(port); // 存儲 port(端口)
// 初始化發送消息
port.postMessage('Hello from SharedWorker!');
// 監聽消息
port.onmessage = function (e) {
const index = ports.indexOf(port); // 獲取當前的port的索引
// const currentPort = ports[index]; // 當前端口
// 向其他端口發送消息
ports.forEach((item, idx) => {
if (index !== idx) {
item.postMessage('消息: ' + e.data);
}
})
};
};
self.onconnect 監聽多個頁面與 SharedWorker 的連接。每當一個新的頁面連接到共享工作線程時,onconnect 事件會被觸發。在 onconnect 事件處理函數中,我們可以通過 event.ports[0] 獲取頁面連接的 port,然後通過 port 進行消息傳遞
在共享線程中瀏覽器控制枱無法顯示任何打印信息:
SharedWorker是一種共享工作線程,它用於處理多個瀏覽器窗口或標籤頁之間的共享計算任務,並且運行在獨立的線程中。工作線程(包括SharedWorker)不具備瀏覽器的 DOM 環境沒有
window對象,所以console、alert等方法都是無法使用的。
如果我們需要調試
SharedWorker,可以使用調試Worker的方法,在瀏覽器地址欄中輸入chrome://inspect/#workers,這樣就可以看到當前頁面中的SharedWorkeredge遊覽器:
edge://inspect/#workers
父頁面 parent.html
<button onclick="send()">向子窗口發送消息</button>
<script>
// 創建 SharedWorker 實例
const worker = new SharedWorker("worker.js");
// 接收來自 worker 的消息
worker.port.onmessage = function (event) {
window.alert(event.data);
};
function send() {
const message = "你好我是父窗口";
worker.port.postMessage(message);
}
</script>
子頁面child.html
<button onclick="send()">向父窗口發送消息</button>
<script>
// 創建 SharedWorker 實例
const worker = new SharedWorker("worker.js");
// 接收來自 worker 的消息
worker.port.onmessage = function (event) {
window.alert(event.data);
};
function send() {
const message = "你好我是父窗口";
worker.port.postMessage(message);
}
</script>
兼容性:
SharedWorker不支持 Internet Explorer,Safari 也存在某些限制,尤其是在移動設備上兼容性較差,在一些老舊瀏覽器中無法使用
總結
瀏覽器多窗口通信有多種方法可供選擇,每種方法都有其適用場景。以下是各方法的優缺點總結:
根據項目需求選擇合適的通信方式,可以大大提升應用的性能和用户體驗。
本文轉載於:https://juejin.cn/post/7459359268523098138
如果對您有所幫助,歡迎您點個關注,我會定時更新技術文檔,大家一起討論學習,一起進步。