Channel Messaging API允許iframe和worker通過通道與主文檔線程進行通信,通過傳遞消息。
- [引言Channel Messaging API](#引言Channel Messaging API)
- iframe的示例
- [Service Worker的示例](#Service Worker的示例)
- 瀏覽器支援
引言Channel Messaging API
在同一個文檔中運行的兩個腳本,但處於不同的上下文中,Channel Messaging API允許它們通過通道進行通信。
此用例涉及以下通信方式:
- 文檔和iframe之間的通信
- 兩個iframe之間的通信
- 兩個文檔之間的通信
它是如何工作的
調用new MessageChannel()
來初始化消息通道。
const channel = new MessageChannel()
該通道具有2個屬性,稱為
- port1
- port2
這些屬性均為MessagePort對象。port1
是由創建通道的一方使用的端口,而port2
是通道接收器使用的端口(順帶一提,通道是雙向的,因此接收器也可以發送消息)。
在通道的每一端,您都可以在一個端口上進行侦聽,並將消息發送到另一個端口。
通過otherWindow.postMessage()
方法發送消息,其中otherWindow
是另一個瀏覽上下文。它接受一個消息,一個源和一個端口。
例如:
const data = { name: 'Flavio' }
const channel = new MessageChannel()
window.postMessage(data, [channel.port2])
消息可以是下列支援的值之一:
- 所有原始類型,但排除符號
- 數組
- 物件文本
- 字符串,日期,正則表達式對象
- Blob,File,FileList對象
- ArrayBuffer,ArrayBufferView對象
- FormData對象
- ImageData對象
- Map和Set對象
“源”是一個URI(例如https://example.org
)。您可以使用'*'
來允許不嚴格的檢查,或指定一個域,或者指定'/'
以設置同一域目標,而無需指定所屬域。
其他瀏覽上下文使用message
事件來聆聽消息:
self.addEventListener('message', event => {
console.log('A new message arrived!')
})
self
在這種情況下與使用window
相同。
在事件處理程序內部,我們可以通過查看事件對象的data
屬性來訪問發送的數據:
self.addEventListener('message', event => {
console.log('A new message arrived!')
console.log(event.data)
})
我們可以通過使用MessagePort.postMessage
來回應:
self.addEventListener('message', event => {
console.log('A new message arrived!')
console.log(event.data)
const data = { someData: 'hey' }
event.ports[0].postMessage(data)
})
通過在端口上調用close()
方法,可以關閉通道:
self.addEventListener('message', event => {
console.log('A new message arrived!')
console.log(event.data)
const data = { someData: 'hey' }
event.ports[0].postMessage(data)
event.ports[0].close()
})
iframe的示例
以下是文檔和嵌入其中的iframe之間發生的通信示例。
主文檔定義一個iframe
和一個span
,我們將在其中打印從iframe
文檔發送的消息。一旦iframe
文檔加載完畢,我們就在我們創建的channel
上給它發送一個消息。
<!DOCTYPE html>
<html>
<body>
<iframe src="iframe.html" width="500" height="500"></iframe>
<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])
}, false)
channel.port1.onmessage = event => {
display.innerHTML = event.data
}
</script>
</html>
iframe頁面的源代碼更加簡單:
<!DOCTYPE html>
<html>
<script>
window.addEventListener('message', event => {
// send a message back
event.ports[0].postMessage('Message back from the iframe')
}, false)
</script>
</html>
正如您所看到的,我們甚至不需要初始化一個channel,因為當從容器頁面接收到消息時,window.onmessage
處理程序會自動運行。
發送的事件由以下屬性組成:
data
:從其他窗口發送的對象origin
:發送消息的窗口的源URIsource
:發送消息的窗口對象
始終驗證消息發送者的來源。
e.ports[0]
是我們在iframe中引用port2
的方式,因為ports
是一個數組,並且該端口被添加到第一個元素。
在我的回答中翻译
Service Worker的示例
Service Worker是事件驅動的worker,其是與web頁面關聯的JavaScript文件。請查看Service Workers指南以了解更多有關它們的信息。
重要的是要知道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)
})
瀏覽器支援
Channel Messaging API當前得到了所有主要瀏覽器的支援,其中很多瀏覽器已經支援了許多年,因此即使是舊版本的瀏覽器也支援它。有關詳細信息,請參閱https://caniuse.com/#feat=channel-messaging。