给定在同一文档中但在不同上下文中运行的两个脚本,Channel Messaging API 允许它们通过通道传递消息进行通信。
调用 new MessageChannel()
消息通道被初始化。
const channel = new MessageChannel()
通道有 2 个属性,称为:
port1
port2
这些属性是 MessagePort
对象。port1
是创建通道的部分使用的端口,port2
是通道接收器使用的端口(顺便说一下,通道是双向的,因此接收器也可以发回消息)。
在通道的每一端,您在一个端口上监听,并将消息发送到另一个端口。
发送消息通过 postMessage
方法:
otherWindow.postMessage()
otherWindow
是其他浏览上下文。它接受消息、来源和端口。例如:
const data = { name: 'O.O' }
const channel = new MessageChannel()
window.postMessage(data, [channel.port2])
消息可以是任何受支持的值:
- 所有基本类型,但不包括
symbol
- 数组
- 对象字面量
- String、Date 和 RegExp 对象
- Blob、File 和 FileList 对象
- ArrayBuffer 和 ArrayBufferView 对象
- FormData 对象
- ImageData 对象
- Map 和 Set 对象
Origin(源)是一个 URI(例如 https://example.com
)。您可以使用 *
来允许不太严格的检查,或指定域,或指定 /
来设置同域目标,而无需指定它是哪个域。
另一个浏览上下文使用 message
事件监听消息:
self.addEventListener('message', (event) => {
console.log('新消息到达!')
})
在这种情况下,self
与使用 window
相同,你也可以省略。
在事件处理程序中,我们可以通过查看事件对象的 data
属性来访问发送的数据:
self.addEventListener('message', (event) => {
console.log('新消息到达!')
console.log(event.data)
})
我们可以使用 MessagePort.postMessage
进行回复:
self.addEventListener('message', (event) => {
console.log('新消息到达!')
console.log(event.data)
const data = { message: 'hey' }
event.ports[0].postMessage(data)
})
可以通过调用端口上的 close()
方法关闭通道:
self.addEventListener('message', (event) => {
console.log('新消息到达!')
console.log(event.data)
const data = { message: 'hey' }
event.ports[0].postMessage(data)
event.ports[0].close()
})
下面是一个在文档和嵌入其中的 iframe
之间进行通信的示例。
主文档定义了一个 iframe
和 span
,我们将在其中打印从 iframe
文档发送的消息。加载 iframe
文档后,我们会在创建的通道上向其发送一条消息。
<!DOCTYPE html>
<html>
<body>
<iframe src="iframe.html" />
<span></span>
</body>
<script>
const channel = new MessageChannel()
const display = document.querySelector('span')
const iframe = document.querySelector('iframe')
iframe.addEventListener('load', () => {
iframe.contentWindow.postMessage('Hey', '*', [channel.port2])
})
channel.port1.onmessage = (event) => {
display.innerHTML = event.data
}
</script>
</html>
iframe
页面内容更简单:
<!DOCTYPE html>
<html>
<script>
window.addEventListener('message', (event) => {
// 发回消息
event.ports[0].postMessage('从 iframe 返回的消息')
})
</script>
</html>
如您所见,我们甚至不需要初始化通道,因为 message
当从容器页面接收到消息时,处理程序会自动运行。
发送的事件由以下属性组成:
data
— 从另一个窗口发送的对象origin
— 发送消息的窗口的原始 URIsource
— 发送消息的窗口对象
始终验证消息发送人的来源。
event.ports[0]
是我们在 iframe
中引用 port2
的方式,因为 ports
是一个数组,并且端口被添加为第一个元素。
Service Worker 是一个事件驱动的 Worker,是一个与网页相关联的 JavaScript 文件。
重要的是要知道 Service Worker 与主线程是隔离的,我们必须使用消息与它们进行通信。
下面是附加到主文档中的脚本处理向 Service Worker 发送消息的方式:
// worker 是已实例化的 Service Worker
const messageChannel = new MessageChannel()
messageChannel.port1.addEventListener('message', (event) => {
console.log(event.data)
})
worker.postMessage(data, [messageChannel.port2])
在 Service Worker 代码中,我们为 message
事件添加了一个事件监听器:
self.addEventListener('message', (event) => {
console.log(event.data)
})
它可以通过向 messageChannel.port2
发送消息来发回消息:
self.addEventListener('message', (event) => {
event.ports[0].postMessage(data)
})