<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          【面試題】2057- 京東一面:瀏覽器跨標簽頁通信的方式都有什么?

          共 18955字,需瀏覽 38分鐘

           ·

          2024-05-25 14:29

              

          跨標簽通信也有很多實際的應(yīng)用場景,比如:

          1. 共享登錄狀態(tài):當用戶在一個標簽頁中登錄后,其他打開的標簽頁需要及時獲取到登錄狀態(tài),以保持一致的用戶體驗。在這種情況下,可以使用瀏覽器的localStorage或sessionStorage來存儲登錄狀態(tài),并通過監(jiān)聽storage事件來實現(xiàn)不同標簽頁之間的狀態(tài)同步。

          2. 實時通知和消息推送:如果用戶在一個標簽頁上收到了新消息或通知,可以通過跨標簽頁通信將該消息或通知傳遞給其他標簽頁。一種常見的處理方式是使用瀏覽器的localStorage或IndexedDB來存儲未讀消息或通知,然后通過監(jiān)聽storage事件或定時輪詢來檢查新消息或通知的變化。

          3. 跨標簽頁數(shù)據(jù)共享:有時候需要在不同的標簽頁之間共享一些數(shù)據(jù),例如購物車數(shù)據(jù)、選項設(shè)置等。這可以通過在localStorage或IndexedDB中存儲數(shù)據(jù),并借助storage事件或定時輪詢來實現(xiàn)數(shù)據(jù)的同步更新。

          4. 標簽頁之間的導航同步:當用戶在一個標簽頁中進行導航操作(例如點擊鏈接或提交表單)時,其他標簽頁可能也需要跟隨導航到相應(yīng)的頁面。這可以通過在標簽頁之間發(fā)送消息或共享狀態(tài)來實現(xiàn)導航的同步。

          在前端中處理瀏覽器跨標簽頁通信時,常用的方法包括:

          • 使用localStorage或sessionStorage存儲共享數(shù)據(jù),并通過監(jiān)聽storage事件來實現(xiàn)數(shù)據(jù)的變化檢測和同步更新。
          • 使用BroadcastChannel API,它提供了一種跨窗口通信的機制,可以在不同標簽頁之間發(fā)送消息。
          • 使用window.postMessage()方法,該方法允許在不同的窗口或標簽頁之間安全地傳遞消息。
          • 借助服務(wù)端的實時通信技術(shù),如WebSocket,通過服務(wù)器作為中介來實現(xiàn)標簽頁之間的消息傳遞和數(shù)據(jù)同步。

          本篇將為你帶來關(guān)于跨瀏覽器標簽通信的詳細分享,以下是正文:


          沒錯,還是京東一面的問題,首先問的是瀏覽器跨標簽也通信的方式有什么,我答完瀏覽器通信通信的方式,后面就接著問 JavaScript 有什么方式的了。

          瀏覽器通信方式

          每個瀏覽器標簽頁通常被視為一個獨立的進程,而不是一個線程。這種多進程架構(gòu)被稱之為多進程瀏覽器,谷歌瀏覽器就是采用這種方式。

          這種架構(gòu)的方式的主要目的是提高瀏覽器的穩(wěn)定性、安全性和性能。

          在多進程瀏覽器中,每個標簽頁都獨立運行在獨立的進程中,這樣一旦一個標簽頁崩潰或遇到問題,不會影響其他標簽頁和瀏覽器本身的穩(wěn)定性。而每個進程都有屬于自己的內(nèi)存。

          在多進程瀏覽器中,不同標簽頁之間的通信是通過進程間通信 IPC 機制來實現(xiàn)的。IPC 是操作系統(tǒng)提供的一種機制,允許不同進程之間交換數(shù)據(jù)和消息,從而實現(xiàn)協(xié)同工作。

          在操作系統(tǒng)中,著有有以下幾種通信方式:

          1. 基于管道的通信:
            • 管道是一種半雙工的通信機制,可用于同一父進程與其子進程之間通信,或者用于同一計算機上的不同進程之間通信。
            • 命名管道提供了進程間進行雙向通信的能力。可以被多個進程打開和使用。其中一個進程將數(shù)據(jù)寫入管道,而另一個進程則可以從管道中讀取這些數(shù)據(jù)。命名管道通常用于在不相關(guān)的進程之間傳遞數(shù)據(jù),比如客戶端和服務(wù)器之間的通信。
            • 匿名管道是一種用于單向通信的機制,僅用于具有父子關(guān)系的進程之間。它只能在創(chuàng)建時通過操作系統(tǒng)提供的機制進行傳遞。匿名管道在創(chuàng)建時自動建立,并且只能用于具有親緣關(guān)系的進程之間的通信。其中一個進程將數(shù)據(jù)寫入管道的寫端,而另一個進程則從管道的讀端讀取這些數(shù)據(jù)。
          2. 消息隊列:消息隊列允許進程通過將消息放入隊列中來進行通信。進程可以從隊列中接收消息,實現(xiàn)異步通信。消息隊列適用于不需要直接的點對點連接的場景,而且可以在不同計算機之間通信。
          3. 共享內(nèi)存:共享內(nèi)存允許多個進程訪問同一塊物理內(nèi)存區(qū)域,從而實現(xiàn)高效的數(shù)據(jù)共享。進程可以在共享內(nèi)存中讀寫數(shù)據(jù),而不需要顯式的數(shù)據(jù)傳輸操作。
          4. 套接字Socket:套接字通信是一種在計算機網(wǎng)絡(luò)中實現(xiàn)進程間通信的方式。它基于網(wǎng)絡(luò)協(xié)議棧,使用 TCP 或 UDP 等傳輸層協(xié)議,在不同的主機之間進行數(shù)據(jù)傳輸和通信。
          5. Remote Procedure Call:RPC 允許一個進程通過網(wǎng)絡(luò)請求調(diào)用另一個進程中的函數(shù),就像調(diào)用本地函數(shù)一樣。遠程過程調(diào)用隱藏了底層通信細節(jié),使得進程間通信更加方便。
          6. 信號(Signal):信號通信是一種在操作系統(tǒng)中實現(xiàn)進程間通信的機制。它允許一個進程向另一個進程發(fā)送信號,用于通知、中斷或請求處理等目的。它是一種異步事件,當某個事件發(fā)生時,操作系統(tǒng)會向進程發(fā)送相應(yīng)的信號。進程可以事先注冊信號處理函數(shù)來捕獲并處理這些信號。

          JavaScript 如何實現(xiàn)跨標簽頁通信

          JavaScript 實現(xiàn)跨標簽頁通信的方式有很多中,接下來我們就來一個一個進行學習。

          BroadcastChannel

          BroadcastChannel 通信的方式原理就是一個命名管道。它允許讓指定的同源下瀏覽器不同的窗口來訂閱它。

          每個 BroadcastChannel 對象都需要使用一個唯一的名稱來標識通道,這個名稱在同一域名下的不同頁面之間必須是唯一的。它允許同一域名下的不同頁面之間進行通信。

          通過 postMessage 方法,一個頁面可以將消息發(fā)送到頻道中,而其他頁面則可以監(jiān)聽 message 事件來接收這些消息。通過這種方式是短線了一種實時通信的機制,可以在不同的頁面之間傳遞信息,實現(xiàn)頁面間的即時交流。如下圖所示:

          20230823064028

          BroadcastChannel 的類型定義有如下代碼所示:

          [Exposed=(Window,Worker)]
          interface BroadcastChannel : EventTarget {
            constructor(DOMString name);

            readonly attribute DOMString name;
            undefined postMessage(any message);
            undefined close();
            attribute EventHandler onmessage;
            attribute EventHandler onmessageerror;
          };

          要想使用,首先我們創(chuàng)建兩個不同的 html 文件分別代表不同的頁面,并且使用 live server 開啟一個本地服務(wù)器:

          <!-- a.html -->

          <!DOCTYPE html>
          <html lang="en">
            <head>
              <meta charset="UTF-8" />
              <meta name="viewport" content="width=device-width, initial-scale=1.0" />
              <title>Document</title>
            </head>
            <body>
              <script>
                const broad = new BroadcastChannel("moment");

                setInterval(() => {
                  broad.postMessage({
                    value`moment ${new Date()}`,
                  });
                }, 3000);

                broad.onmessage = function (e{
                  console.log(e.data);
                };
              
          </script>
            </body>
          </html>

          <!-- b.html -->

          <!DOCTYPE html>
          <html lang="en">
            <head>
              <meta charset="UTF-8" />
              <meta name="viewport" content="width=device-width, initial-scale=1.0" />
              <title>Document</title>
            </head>
            <body>
              <script>
                const broad = new BroadcastChannel("moment");
                let index = 1;
                setInterval(() => {
                  broad.postMessage({
                    value`supper ${index++}`,
                  });
                }, 3000);

                broad.onmessage = function (e{
                  console.log(e.data);
                };
              
          </script>
            </body>
          </html>

          代碼編寫完了,我們來看看代碼允許效果,如下圖所示:

          動畫.gif

          通過 postMessage 向管道中發(fā)送消息,當管道中存在消息的時候,可以通過 onmessage 方法獲取到信息內(nèi)容。

          Service Worker

          Service Worker 它是一種服務(wù)工作線程,是一種在瀏覽器背后運行的腳本,用于處理網(wǎng)絡(luò)請求和緩存等任務(wù)。它是一種在瀏覽器與網(wǎng)絡(luò)之間的中間層,允許開發(fā)者攔截和控制頁面發(fā)出的網(wǎng)絡(luò)請求,以及管理緩存,從而實現(xiàn)離線訪問、性能優(yōu)化和推送通知等功能。

          它在瀏覽器背后獨立運行與網(wǎng)頁分開,這意味著即使用戶關(guān)閉了網(wǎng)頁,Service Worker 仍然可以運行。可以用于實現(xiàn)推送通知功能。它可以注冊為推送消息的接收者,當服務(wù)器有新的通知要發(fā)送時,Service Worker 可以顯示通知給用戶,即使網(wǎng)頁沒有打開。

          要想使用,首先我們創(chuàng)建兩個不同的 html 文件分別代表不同的頁面,創(chuàng)建一個 Service Worker 文件,并且使用 live server 開啟一個本地服務(wù)器:

          <!-- a.html -->

          <!DOCTYPE html>
          <html lang="en">
            <head>
              <meta charset="UTF-8" />
              <meta name="viewport" content="width=device-width, initial-scale=1.0" />
              <title>Document</title>
            </head>
            <body>
              <script>
                navigator.serviceWorker.register("worker.js").then(() => {
                  console.log("注冊成功");
                });

                setInterval(() => {
                  navigator.serviceWorker.controller.postMessage({
                    value`moment ${new Date()}`,
                  });
                }, 3000);

                navigator.serviceWorker.onmessage = function (e{
                  console.log(e.data.value);
                };
              
          </script>
            </body>
          </html>

          <!-- b.html -->

          <!DOCTYPE html>
          <html lang="en">
            <head>
              <meta charset="UTF-8" />
              <meta name="viewport" content="width=device-width, initial-scale=1.0" />
              <title>Document</title>
            </head>
            <body>
              <script>
                navigator.serviceWorker.register("worker.js").then(() => {
                  console.log("注冊成功");
                });

                setInterval(() => {
                  navigator.serviceWorker.controller.postMessage({
                    value`moment ${new Date()}`,
                  });
                }, 3000);

                navigator.serviceWorker.onmessage = function (e{
                  console.log(e.data.value);
                };
              
          </script>
            </body>
          </html>

          創(chuàng)建一個 worker.js 文件并編寫以下代碼:

          // worker.js
          self.addEventListener("message"function (e{
            e.waitUntil(
              self.clients.matchAll().then(function (clients{
                if (!clients || clients.length === 0) {
                  return;
                }
                clients.forEach(function (client{
                  client.postMessage(e.data);
                });
              })
            );
          });

          你所編寫的 Service Worker 將遵守以下生命周期:

          1. 注冊: 在網(wǎng)頁的 JavaScript 代碼中調(diào)用 navigator.serviceWorker.register() 方法來注冊一個 Service Worker;

          2. 安裝: 當 Service Worker 文件被下載并首次運行時,會觸發(fā) install 事件。在 install 事件中,你可以緩存靜態(tài)資源,如 HTML、CSS、JavaScript 文件,以便在離線時使用;

          3. 激活: 安裝成功后,Service Worker 并不會立即接管頁面的網(wǎng)絡(luò)請求。它需要等到之前的所有頁面都關(guān)閉,或者在下次頁面加載時才會激活();

            就是因為編寫的 worker 代碼不生效,一直刷新都不生效,直到我關(guān)機重啟了才生效的......

          4. 控制: 一旦 Service Worker 被激活,它就開始控制在其作用域內(nèi)的頁面。它可以攔截頁面發(fā)出的網(wǎng)絡(luò)請求,并根據(jù)緩存策略返回緩存的內(nèi)容;

          5. 更新: 當你更新 Service Worker 文件并再次注冊時,會觸發(fā)一個新的 install 事件。你可以在新的 install 事件中更新緩存,然后在下次頁面加載時進行激活,以確保新的 Service Worker 被使用;

          6. 解除注冊: 如果你不再需要 Service Worker,可以通過調(diào)用 navigator.serviceWorker.unregister() 來解除注冊;

          它本身是一個由 promise 封裝的對象,未初始化時是一個 pending 狀態(tài)的,當成功注冊之后會變成 fulfilled,并且對外暴露以下方法,如下圖所示:

          20230823084407

          localStorage

          在 Web Storage 中,每一次將一個值存儲到本地存儲時,都會觸發(fā)一個 storage 事件,由事件監(jiān)聽器發(fā)送給回調(diào)函數(shù)的事件對象有如下圖所示:

          20230823085308

          其中有幾個自動填充的屬性如下:

          • key:被修改的鍵;
          • newValue:修改后的新值;
          • oldValue:修改前的值;
          • storageArea:事件監(jiān)聽對應(yīng)的 Storage 對象

          編寫如下代碼,省略部分代碼:

          <!-- a.html -->
          <script>
            let index = 0;
            setInterval(() => {
              localStorage.moment = `moment ${index++}`;
            }, 1000);

            window.addEventListener("storage", (e) => {
              console.log("被修改的鍵: ", e.key);
              console.log("舊值: ", e.oldValue);
              console.log("新值: ", e.newValue);
            });
          </script>

          <!-- b.html -->
          <script>
            let index = 0;
            setInterval(() => {
              localStorage.supper = `supper ${index++}`;
            }, 1000);

            window.addEventListener("storage", (e) => {
              console.log("被修改的鍵: ", e.key);
              console.log("舊值: ", e.oldValue);
              console.log("新值: ", e.newValue);
              console.log("----------");
            });
          </script>


          SharedWorker

          SharedWorker 是一種在 Web 瀏覽器中使用的 Web API,它允許不同的瀏覽上下文,如不同的瀏覽器標簽頁之間共享數(shù)據(jù)和執(zhí)行代碼。它可以用于在多個瀏覽上下文之間建立通信通道,以便它們可以共享信息和協(xié)同工作。

          與普通的 Worker 不同,SharedWorker 可以在多個瀏覽上下文中實例化,而不僅限于一個單獨的瀏覽器標簽頁或框架。這使得多個瀏覽上下文可以共享同一個后臺線程,從而更有效地共享數(shù)據(jù)和資源,而不必在每個標簽頁或框架中都創(chuàng)建一個獨立的工作線程。

          要想使用它,首先編寫如下代碼,省略部分代碼:

          <!-- a.html -->
          <script>
            let index = 0;
            const worker = new SharedWorker("worker.js");

            setInterval(() => {
              worker.port.postMessage(`moment ${index++}`);
            }, 1000);
          </script>

          <!-- b.html -->
          <script>
            const worker = new SharedWorker("worker.js");

            worker.port.start();
            setInterval(() => {
              worker.port.postMessage("php是世界上最好的語言");
            }, 1000);

            worker.port.onmessage = function (e{
              if (e.data) {
                console.log(e.data);
              }
            };
          </script>

          創(chuàng)建一個 worker.js 文件,并編寫以下代碼:

          let data = "";

          self.onconnect = (e) => {
            const port = e.ports[0];

            port.onmessage = function (e{
              if (e.data === "php是世界上最好的語言") {
                port.postMessage(data);
                data = "";
              } else {
                data = e.data;
              }
            };
          };

          IndexDB

          IndexedDB 是一種在瀏覽器中用于存儲和管理大量結(jié)構(gòu)化數(shù)據(jù)的 Web API。它提供了一種持久性存儲解決方案,允許 Web 應(yīng)用程序在客戶端存儲數(shù)據(jù),以便在不同會話、頁面加載或瀏覽器關(guān)閉之間保留數(shù)據(jù)。

          與傳統(tǒng)的 cookie 或 localStorage 等存儲方式不同,IndexedDB 更適合存儲復(fù)雜的、結(jié)構(gòu)化的數(shù)據(jù),例如對象、數(shù)組、鍵值對等。這使得它特別適用于應(yīng)用程序需要存儲大量數(shù)據(jù)、執(zhí)行高級查詢或支持離線工作的情況。

          要實現(xiàn)跨標簽通信,如下代碼所示:

          <!-- a.html -->
          <script>
            let index = 0;
            // 打開或創(chuàng)建 IndexedDB 數(shù)據(jù)庫
            const request = indexedDB.open("database"1);

            request.onupgradeneeded = (event) => {
              const db = event.target.result;
              const objectStore = db.createObjectStore("dataStore", {
                keyPath"key",
              });
            };

            request.onsuccess = (event) => {
              const db = event.target.result;
              const transaction = db.transaction(["dataStore"], "readwrite");
              const objectStore = transaction.objectStore("dataStore");

              // 存儲數(shù)據(jù)

              objectStore.put({ key"supper"value`moment` });

              transaction.oncomplete = () => {
                db.close();
              };
            };
          </script>

          <!-- b.html -->
          <script>
            // 打開相同的 IndexedDB 數(shù)據(jù)庫
            const request = indexedDB.open("database"1);

            request.onsuccess = (event) => {
              const db = event.target.result;
              const transaction = db.transaction(["dataStore"], "readonly");
              const objectStore = transaction.objectStore("dataStore");

              // 獲取數(shù)據(jù)
              const getRequest = objectStore.get("supper");

              getRequest.onsuccess = (event) => {
                const data = event.target.result;
                if (data) {
                  console.log(data.value);
                }
              };

              transaction.oncomplete = () => {
                db.close();
              };
            };
          </script>

          最終代碼運行如下圖所示:

          20230823095933

          cookie

          cookie 的話沒什么好講的,直接上代吧:

          <!-- a.html -->
          <script>
            let index = 0;
            setInterval(() => {
              document.cookie = `supper=moment ${index++}`;
            }, 1000);
          </script>

          <!-- b.html -->
          <script>
            console.log("cookie 的值為: "document.cookie);

            setInterval(() => {
              console.log("cookie 的值發(fā)生了變化: "document.cookie);
            }, 1000);
          </script>

          具體代碼運行效果如下圖所示:

          動畫.gif

          postMessage

          window.postMessage() 方法可以安全地實現(xiàn)跨源通信。通常,對于兩個不同頁面的腳本,只有同源時,這兩個腳本才能相互通信。

          <!-- a.html -->
          <body>
            <button class="pop">彈出新窗口</button>
            <button class="button">發(fā)送數(shù)據(jù)</button>
            <script>
              const pop = document.querySelector(".pop");
              const button = document.querySelector(".button");

              let index = 0;
              let opener = null;

              pop.addEventListener("click", () => {
                opener = window.open(
                  "b.html",
                  "123",
                  "height=600,width=600,top=20,resizeable=yes"
                );
              });

              button.addEventListener("click", () => {
                const data = {
                  value`moment ${index++}`,
                };

                opener.postMessage(data, "*");
              });
            
          </script>
          </body>

          <!-- b.html -->
          <body>
            <div>moment</div>
            <script>
              window.addEventListener("message", (e) => {
                console.log(e.data);
              });
            
          </script>
          </body>

          通過點擊按鈕在主窗口和彈出的新窗口之間進行通信。通過 postMessage,主窗口可以向新窗口發(fā)送數(shù)據(jù),從而實現(xiàn)了簡單的跨窗口通信。在實際應(yīng)用中,你可以在接收消息的窗口中監(jiān)聽 message 事件,然后在事件處理程序中處理接收到的數(shù)據(jù)。

          總結(jié)

          文章中涉及到的大部分名詞解釋來自 mdn。

          JavaScript 跨標簽通信允許不同的瀏覽器標簽頁之間進行數(shù)據(jù)傳遞和通信,為構(gòu)建更復(fù)雜和協(xié)同的 Web 應(yīng)用程序提供了一種機制。通過適當?shù)募夹g(shù)和策略,我們可以實現(xiàn)有效的跨標簽通信,以滿足不同應(yīng)用場景的需求。

          原文地址:https://juejin.cn/post/7270155117705510968

          作者:Moment

          最后


          瀏覽 47
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  天天曰夜夜爽天天操 | 成人精品三级AV在线看 | 日日69| 欧美成人精品在线观看 | 国产模特日逼视频 |