Exploring the Broadcast Channel API for cross-tab communication title. A gradient background with a JavaScript logo in the bottom-left corner and an icon representing a browser in the top-right corner.
赞助

探索 Broadcast Channel API 用于跨标签页通信

阅读时间 4 分钟

Broadcast Channel API 实现了不同浏览器窗口、标签页、iframe 和 Web Worker 之间的通信。它为浏览器中多个 上下文 之间的数据和行为同步提供了一种简单高效的方式,从而构建更具响应性和吸引力的 Web 应用程序。

在本文中,我们将探讨 Broadcast Channel API 的概念、用法和实际应用。我们还将通过一个实际示例,构建一个小型应用程序,演示如何使用该 API 向不同的标签页和窗口发送消息。

理解 Broadcast Channel API

Broadcast Channel API 引入了一种机制,允许同一用户和同一浏览器的、同一来源(origin)下的不同上下文之间进行通信。它基于创建单个共享通道的原理,多个浏览器上下文可以随时加入和离开该通道。

加入后,这些上下文可以通过通道发送和接收消息,从而实现无缝的数据交换和事件传播。这种机制消除了对复杂服务器端通信的需求。以下是使用该 API 的简要说明。

创建或加入频道

js
const bc = new BroadcastChannel("test_channel");

发送消息

js
bc.postMessage("This is a test message");

接收消息(有关详细信息,请参阅 BroadcastChannel: message event

js
bc.onmessage = (event) => {
  console.log(event.data);
  // { method: "add", note: "This is a test message" }
};

构建 Node.js 应用程序

首先,请按照我们上一篇文章中“在 Vultr 上部署服务器”部分中的步骤部署一个服务器。接下来,通过 SSH 访问服务器终端,并为我们的 Web 应用程序设置一个项目。

我们将使用 Nano 文本编辑器在服务器上创建和编辑项目文件。您可以在 快捷键备忘单 中查找 Nano 的使用帮助。我们还将使用 Uncomplicated Firewall (UFW) 来控制允许进出服务器的流量。在我们的应用程序中,我们使用 Node.js 来提供应用程序的索引,并使用 http-server 运行应用程序。任何其他类型的服务器,如 Python 和 Apache,也可以达到相同的目的。我们还使用端口 8080,通过 UFW 仅允许通过此端口进入流量。

  1. 创建一个项目目录,并进入该目录。

    bash
    mkdir notes-app
    cd notes-app
    
  2. 初始化一个 Node.js 项目。

    bash
    npm init -y
    
  3. 安装 HTTP 服务器依赖项。

    bash
    npm install http-server
    
  4. 创建一个 HTML 文件。

    bash
    nano index.html
    
  5. 将以下代码复制并粘贴到 index.html 文件中。

    html
    <!doctype html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Note-taking App</title>
        <link rel="stylesheet" href="styles.css" />
      </head>
      <body>
        <h1>Note-taking App</h1>
        <div id="noteList"></div>
        <div id="noteForm">
          <label for="noteInput">New note</label>
          <input type="text" id="noteInput" placeholder="A note..." />
          <button id="addNoteButton">Add Note</button>
          <button id="resetNoteButton">Reset Notes</button>
        </div>
        <script src="app.js"></script>
      </body>
    </html>
    
  6. 保存并退出文件。

  7. 创建一个 CSS 文件。

    bash
    nano styles.css
    
  8. 将以下代码复制并粘贴到 styles.css 文件中。

    css
    body {
      font-family: Arial, sans-serif;
      background-color: #f4f4f4;
      margin: 0;
      padding: 20px;
    }
    
    h1 {
      color: #333;
      text-align: center;
    }
    
    #noteList {
      display: grid;
      row-gap: 10px;
      background-color: #fff;
      border: 1px solid #ddd;
      border-radius: 5px;
      padding: 10px;
      margin-bottom: 20px;
    }
    
    #noteList div {
      background-color: #f9f9f9;
      border: 1px solid #ddd;
      border-radius: 3px;
      padding: 10px;
    }
    
    #noteForm {
      display: grid;
      column-gap: 10px;
      align-items: center;
      grid-template-columns: max-content 1fr max-content max-content;
    }
    
    #noteInput {
      padding: 10px;
      border: 1px solid #ddd;
      border-radius: 3px;
      font-size: 16px;
    }
    
    button {
      padding: 10px 20px;
      background-color: #4caf50;
      color: #fff;
      border: none;
      border-radius: 3px;
      font-size: 16px;
      cursor: pointer;
    }
    
    button:hover {
      background-color: #45a049;
    }
    
  9. 保存并退出文件。

实现 Broadcast Channel API

  1. notes-app 目录中,创建一个 JavaScript 文件。

    bash
    nano app.js
    
  2. 将以下 JavaScript 代码复制并粘贴到 app.js 中。

    js
    const noteList = document.getElementById("noteList");
    const noteInput = document.getElementById("noteInput");
    const addNoteButton = document.getElementById("addNoteButton");
    const resetNoteButton = document.getElementById("resetNoteButton");
    
    let notes = [];
    
    function renderNotes() {
      noteList.innerHTML = "";
    
      notes.forEach((note) => {
        const noteItem = document.createElement("div");
        noteItem.textContent = note;
        noteList.appendChild(noteItem);
      });
    }
    
    addNoteButton.addEventListener("click", () => {
      const newNote = noteInput.value.trim();
    
      if (newNote) {
        notes.push(newNote);
        renderNotes();
        noteInput.value = "";
    
        channel.postMessage({ action: "add", note: newNote });
      }
    });
    
    resetNoteButton.addEventListener("click", () => {
      notes = [];
      renderNotes();
    
      channel.postMessage({ action: "reset" });
    });
    
    const channel = new BroadcastChannel("notes-channel");
    
    channel.addEventListener("message", (event) => {
      const { action, note } = event.data;
    
      if (action === "add") {
        notes.push(note);
        renderNotes();
      } else if (action === "reset") {
        notes = [];
        renderNotes();
      }
    });
    
  3. 保存并退出文件。

  4. 允许端口 8080 的传入连接

    bash
    sudo ufw allow 8080
    
  5. 启动文件服务器。

    bash
    npx http-server
    
  6. 访问应用程序 URL http://<server-ip>:8080

现在您可以并排打开两个浏览器窗口或标签页。在一个页面上添加一个笔记,您会发现该笔记出现在第二个标签页中,而无需刷新页面。尝试重置所有笔记,您会发现笔记从两个标签页中都被删除了,而无需刷新。

让我们看一下我们在 app.js 中编写的代码。renderNotes 函数为每个添加的笔记创建一个元素。addNoteButton 函数允许我们在应用程序中添加笔记,channel.postMessage 将“add”操作广播到其他窗口或标签页。同样,resetNoteButton 允许我们删除所有现有笔记,channel.postMessage 将“reset action”广播到其他窗口或标签页。

最后,创建一个名为 'notes-channel' 的新 BroadcastChannel,允许同一来源下不同窗口/标签页之间进行通信。BroadcastChannel 的事件监听器监听来自频道的 message 事件,并根据提供的信息采取相应的操作。

实际用例和示例

  • 在新闻和媒体网站中
    • 用例:跨多个窗口同步文章的阅读进度。
    • 示例:用户可以在一个窗口中开始阅读文章,并在另一个窗口或标签页中从同一点无缝继续阅读,从而实现一致的阅读体验。
  • 在生产力应用中
    • 用例:在多个上下文之间实现文档或文件的更改的实时同步。
    • 示例:在协作文本编辑器中,一个用户所做的更改可以实时广播到其他上下文。
  • 在社交媒体平台上
    • 用例:跨多个标签页或窗口通知用户新更新、消息或通知。
    • 示例:如果用户为社交媒体平台打开了多个标签页,他们可以在所有上下文中接收实时更新,确保他们不会错过重要信息。

总结

在本文中,我们探讨了 Broadcast Channel API 的概念、用法和实际实现。我们构建了一个基本的同步笔记应用程序,并学习了如何使用 Broadcast Channel API 来构建互联的 Web 体验。

这是一篇 Vultr 的赞助文章。Vultr 是全球最大的私营云计算平台。Vultr 深受开发者喜爱,已为 185 个国家的 150 万多客户提供服务,提供灵活、可伸缩的全球云计算、云 GPU、裸金属和云存储解决方案。了解更多关于 Vultr 的信息。