使用通道消息传递

注意:此功能在 Web Workers 中可用。

The Channel Messaging API 允许两个在同一文档的不同浏览上下文中运行的独立脚本(例如,两个 <iframe> 元素、主文档和单个 <iframe>,或通过 SharedWorker 的两个文档)直接通信,通过双向通道(或管道)传递彼此之间的消息,每个通道的末端都有一个端口。

在本文中,我们将探讨使用此技术的入门知识。

用例

通道消息传递在以下情况下特别有用:您有一个社交网站,通过 iframe 将其他网站的功能嵌入到其主界面中,例如游戏、通讯录或带有个性化音乐选择的音频播放器。当这些功能作为独立单元时,一切正常,但当您希望主网站与 <iframe> 元素之间,或不同 <iframe> 元素之间进行交互时,就会出现困难。例如,如果您想从主网站向通讯录添加联系人,将游戏的高分添加到您的主资料中,或将音频播放器中的新背景音乐选择添加到游戏中,该怎么办?由于 Web 使用的安全模型,使用传统的 Web 技术来完成这些操作并不容易。您必须考虑来源是否彼此信任,以及消息是如何传递的。

另一方面,消息通道可以提供安全的通道,允许您在不同的浏览上下文中传递数据。

注意:有关更多信息和想法,规范中的 将端口作为 Web 上对象能力模型的基础 部分是一个有用的阅读材料。

简单示例

为了帮助您入门,我们在 GitHub 上发布了一些演示。首先,查看我们的 通道消息传递基本演示 (在线运行),该演示展示了在页面和嵌入的 <iframe> 之间进行非常简单的单消息传输。

其次,查看我们的 多消息传递演示 (在线运行),该演示展示了更复杂的设置,可以在主页面和 IFrame 之间发送多个消息。

在本文中,我们将重点介绍后一个示例,它看起来像

Demo with "Hello this is my demo" sent as five separate messages. The messages are displayed as a bulleted list.

创建通道

在演示的主页面中,我们有一个简单的表单,带有一个文本输入框,用于输入要发送到 <iframe> 的消息。我们还有一个段落,我们将在稍后使用它来显示从 <iframe> 收回的确认消息。

js
const input = document.getElementById("message-input");
const output = document.getElementById("message-output");
const button = document.querySelector("button");
const iframe = document.querySelector("iframe");

const channel = new MessageChannel();
const port1 = channel.port1;

// Wait for the iframe to load
iframe.addEventListener("load", onLoad);

function onLoad() {
  // Listen for button clicks
  button.addEventListener("click", onClick);

  // Listen for messages on port1
  port1.onmessage = onMessage;

  // Transfer port2 to the iframe
  iframe.contentWindow.postMessage("init", "*", [channel.port2]);
}

// Post a message on port1 when the button is clicked
function onClick(e) {
  e.preventDefault();
  port1.postMessage(input.value);
}

// Handle messages received on port1
function onMessage(e) {
  output.innerHTML = e.data;
  input.value = "";
}

我们首先使用 MessageChannel() 构造函数创建一个新的消息通道。

当 IFrame 加载时,我们为按钮注册一个 onclick 处理程序,为 MessageChannel.port1 注册一个 onmessage 处理程序。最后,我们使用 window.postMessage 方法将 MessageChannel.port2 传输到 IFrame。

让我们详细探讨 iframe.contentWindow.postMessage 行是如何工作的。它接受三个参数

  1. 要发送的消息。对于这个初始端口传输,此消息可以为空字符串,但在本示例中,它被设置为 'init'
  2. 要发送消息的来源。* 表示“任何来源”。
  3. 一个对象,其所有权将被转移到接收浏览上下文。在本例中,我们将 MessageChannel.port2 传输到 IFrame,以便它可以用于与主页面通信。

当我们的按钮被点击时,我们阻止表单以正常方式提交,然后通过 MessageChannel 将文本输入框中输入的值发送到 IFrame。

在 IFrame 中接收端口和消息

<iframe> 元素中,我们有以下 JavaScript 代码

js
const list = document.querySelector("ul");
let port2;

// Listen for the initial port transfer message
window.addEventListener("message", initPort);

// Setup the transferred port
function initPort(e) {
  port2 = e.ports[0];
  port2.onmessage = onMessage;
}

// Handle messages received on port2
function onMessage(e) {
  const listItem = document.createElement("li");
  listItem.textContent = e.data;
  list.appendChild(listItem);
  port2.postMessage(`Message received by IFrame: "${e.data}"`);
}

当通过 window.postMessage 方法从主页面接收到初始消息时,我们将运行 initPort 函数。这将保存已传输的端口,并注册一个 onmessage 处理程序,该处理程序将在每次通过我们的 MessageChannel 传递消息时被调用。

当从主页面接收到消息时,我们创建一个列表项并将其插入无序列表中,将列表项的 textContent 设置为事件的 data 属性(其中包含实际消息)。

接下来,我们通过调用 MessagePort.postMessage 在最初传输到 iframe 的 MessageChannel.port2 上,将确认消息发布回主页面。

在主页面接收确认

回到主页面,现在让我们看一下 onmessage 处理程序函数。

js
// Handle messages received on port1
function onMessage(e) {
  output.innerHTML = e.data;
  input.value = "";
}

当从 IFrame 接收到确认原始消息已成功接收的消息时,这将确认输出到段落,并将文本输入框清空,准备发送下一条消息。

规范

规范
HTML 标准
# message-channels
HTML 标准
# message-ports

浏览器兼容性

api.MessageChannel

BCD 表仅在浏览器中加载

api.MessagePort

BCD 表仅在浏览器中加载

另请参阅