动态

详情 返回 返回

記錄---瀏覽器多窗口通信有效實踐總結 - 动态 详情

🧑‍💻 寫在開頭

點贊 + 收藏 === 學會🤣🤣🤣

如何跨越不同窗口通信

在現代 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-serverLive 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.openpostMessage 在大多數現代瀏覽器中得到支持。需要注意:如果打開的窗口被瀏覽器阻止彈出,通信無法進行。

2、客户端存儲 + 定時器實時刷新監聽【不推薦】

通過客户端存儲(如 cookielocalStoragesessionStorage)結合定時器(如 setIntervalrequestAnimationFrame),可以實現同源域名下不同窗口之間的通信。此方式利用定時器定時檢查存儲值的變化,但缺點是浪費性能資源,且數據更新不夠實時

父頁面 parent.html

// 父頁面向 storage 寫入數據
localStorage.setItem('message', 'Hello from Page');
 
子頁面child.html
// 子頁面 定時檢查 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對象,所以consolealert等方法都是無法使用的。

如果我們需要調試SharedWorker,可以使用調試Worker的方法,在瀏覽器地址欄中輸入chrome://inspect/#workers,這樣就可以看到當前頁面中的SharedWorker

edge遊覽器: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

如果對您有所幫助,歡迎您點個關注,我會定時更新技術文檔,大家一起討論學習,一起進步。

 

user avatar toopoo 头像 kongsq 头像 asmallwhitecat 头像 wubomu 头像 robin_ren 头像 talkcss 头像 zengjingdeshaonian 头像 user_4jsjtpci 头像 hello888 头像 htvlz 头像 hai2007 头像
点赞 11 用户, 点赞了这篇动态!
点赞

Add a new 评论

Some HTML is okay.