使用通道消息
注意:此功能在 Web Workers 中可用。
该通道消息 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
属性(其中包含实际消息)。
接下来,我们通过调用最初传输到 iframe 的MessageChannel.port2
上的MessagePort.postMessage
,将一条确认消息通过消息通道发送回主页面。
在主页面中接收确认
回到主页面,现在让我们看一下onmessage
处理程序函数。
// Handle messages received on port1
function onMessage(e) {
output.innerHTML = e.data;
input.value = "";
}
当从 IFrame 收到确认原始消息已成功接收的消息时,这会将确认输出到段落,并清空文本输入框,以便发送下一条消息。
规范
规范 |
---|
HTML # message-channels |
HTML # message-ports |
浏览器兼容性
api.MessageChannel
加载中…
api.MessagePort
加载中…