使用通道消息传递
注意:此功能在 Web Workers 中可用。
The Channel Messaging API 允许两个在同一文档的不同浏览上下文中运行的独立脚本(例如,两个 <iframe>
元素、主文档和单个 <iframe>
,或通过 SharedWorker
的两个文档)直接通信,通过双向通道(或管道)传递彼此之间的消息,每个通道的末端都有一个端口。
在本文中,我们将探讨使用此技术的入门知识。
用例
通道消息传递在以下情况下特别有用:您有一个社交网站,通过 iframe 将其他网站的功能嵌入到其主界面中,例如游戏、通讯录或带有个性化音乐选择的音频播放器。当这些功能作为独立单元时,一切正常,但当您希望主网站与 <iframe>
元素之间,或不同 <iframe>
元素之间进行交互时,就会出现困难。例如,如果您想从主网站向通讯录添加联系人,将游戏的高分添加到您的主资料中,或将音频播放器中的新背景音乐选择添加到游戏中,该怎么办?由于 Web 使用的安全模型,使用传统的 Web 技术来完成这些操作并不容易。您必须考虑来源是否彼此信任,以及消息是如何传递的。
另一方面,消息通道可以提供安全的通道,允许您在不同的浏览上下文中传递数据。
注意:有关更多信息和想法,规范中的 将端口作为 Web 上对象能力模型的基础 部分是一个有用的阅读材料。
简单示例
为了帮助您入门,我们在 GitHub 上发布了一些演示。首先,查看我们的 通道消息传递基本演示 (在线运行),该演示展示了在页面和嵌入的 <iframe>
之间进行非常简单的单消息传输。
其次,查看我们的 多消息传递演示 (在线运行),该演示展示了更复杂的设置,可以在主页面和 IFrame 之间发送多个消息。
在本文中,我们将重点介绍后一个示例,它看起来像
创建通道
在演示的主页面中,我们有一个简单的表单,带有一个文本输入框,用于输入要发送到 <iframe>
的消息。我们还有一个段落,我们将在稍后使用它来显示从 <iframe>
收回的确认消息。
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
行是如何工作的。它接受三个参数
- 要发送的消息。对于这个初始端口传输,此消息可以为空字符串,但在本示例中,它被设置为
'init'
。 - 要发送消息的来源。
*
表示“任何来源”。 - 一个对象,其所有权将被转移到接收浏览上下文。在本例中,我们将
MessageChannel.port2
传输到 IFrame,以便它可以用于与主页面通信。
当我们的按钮被点击时,我们阻止表单以正常方式提交,然后通过 MessageChannel
将文本输入框中输入的值发送到 IFrame。
在 IFrame 中接收端口和消息
在 <iframe>
元素中,我们有以下 JavaScript 代码
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
处理程序函数。
// 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 表仅在浏览器中加载