跨文档通信(cross-document messaging)、worker通信(cross-worker messaging)、通道通信(channel messaging)
消息事件 MessageEvent()
用于:
Window.postMessage()
和 Window.onmessage
).MessagePort.postMessage()
(en-US) 和MessagePort.onmessage
).Worker.postMessage()
, Worker.onmessage
, ServiceWorkerGlobalScope.onmessage
(en-US), 等等.)onmessage
属性).EventSource.onmessage
(en-US)).Broadcastchannel.postMessage()
) 和 BroadcastChannel.onmessage
).RTCDataChannel.onmessage
(en-US)).属性:
属性 | 说明 |
---|---|
data | 包含任意字符串数据,由原始脚本发送 |
origin | 一个字符串,包含原始文档的方案、域名以及端口(如:http://domain.example:80) |
lastEventId | 一个字符串,包含了当前的消息事件的唯一标识符。 |
source | 原始文件的窗口的引用。更确切地说,它是一个WindowProxy对象。 |
ports | 一个数组,包含任何MessagePort对象发送消息。 |
最常见的例子 iframe 之间。
otherWindow.postMessage(message, targetOrigin, [transfer])
window.addEventListener("message", (messageEvent) => {}, false)
targetOrigin:通过窗口的origin属性来指定哪些窗口能接收到消息事件,其值可以是字符串"*"(表示无限制)或者一个URI。如果你明确的知道消息应该发送到哪个窗口,那么请始终提供一个有确切值的targetOrigin,而不是*。不提供确切的目标将导致数据泄露到任何对数据感兴趣的恶意站点。
主页面
<iframe src="iframe_1.html">iframe>
<iframe src="iframe_2.html">iframe>
iframe_1.html
<input id="data" />
<button id="btn">发送button>
<script>
let btn = document.querySelector('#btn')
btn.addEventListener('click', function () {
let data = document.querySelector('#data')
window.parent.frames[1].postMessage(data.value, '*')
return false
})
script>
iframe_2.html
接收到的数据:<span id="message">span>
<script>
let msg = document.querySelector('#message')
window.addEventListener('message', (msgEvent) => {
msg.innerHTML = msgEvent.data
})
script>
一种可由脚本创建的后台任务,任务执行中可以向其创建者收发信息。
worker.postMessage(aMessage, transferList)
worker.onmessage = (msgEvent) => {} // ①
worker.addEventListener('message', (msgEvent) => {}) // ②
主页面
const worker = new Worker('worker.js')
worker.postMessage({
num1: num1.value,
num2: num2.value
})
worker.onmessage = function (msgEvent) {
dom.innerHTML = msgEvent.data
}
worker.js
onmessage = function (msgEvent) {
let {num1, num2} = msgEvent.data
postMessage(Number(num1) + Number(num2))
}
function createWorker(workerFunc) {
if (! (workerFunc instanceof Function)) {
throw new Error('Argument must be function');
}
const src = `(${workerFunc})();`;
const blob = new Blob([src], {type: 'application/javascript'});
const url = URL.createObjectURL(blob);
return new Worker(url);
}
MessageChannel
接口允许我们创建一个新的消息通道,并通过它的两个MessagePort
属性发送数据。
实现上述双 iframe 示例:
Port1[iframe2
] <===> Port2[iframe1
]
const [ifr1, ifr2] = document.querySelectorAll('iframe')
const ifr1Window = ifr1.contentWindow
const ifr2Window = ifr2.contentWindow
// 监听【iframe】onmessage 信息
ifr1Window.addEventListener("message", function (msgEvent) {
ifr1Window.document.querySelector('#btn').addEventListener('click', function () {
// 根据【port2】进行传值
msgEvent.ports[0].postMessage(ifr1Window.document.querySelector('#data').value)
})
})
ifr2Window.addEventListener("load", function () {
// 建立信息通道
const channel = new MessageChannel()
// 监听【port1】的信息
channel.port1.onmessage = function (msgEvent) {
ifr2Window.document.querySelector('#message').innerHTML = msgEvent.data
}
// 给 iframe1 发送信息【携带 port2 】
ifr1Window.postMessage('I am ready', '*', [channel.port2])
})
可以实现上述多 iframe
之间传值,更重要的是其可以实现多 web worker
之间传值。
主页面
const channel = new MessageChannel()
const w1 = new Worker('worker1.js')
const w2 = new Worker('worker2.js')
w1.postMessage('index', [channel.port1])
w2.postMessage('', [channel.port2])
w2.onmessage = function (msgEvent) {
console.log(msgEvent.data)
}
worker1.js
onmessage = function ({data, ports}) {
const port1 = ports[0]
port1.postMessage(`${data} => worker1`)
}
worker2.js
onmessage = function({ports}) {
const port2 = ports[0]
port2.onmessage = function (msgEvent) {
postMessage(`${msgEvent.data} => worker2`)
}
}
index => worker1 => worker2
Comlink 采用的 RPC 代理方式,并不是传递上下文环境(因为这是非常危险的,而且函数传递时会导致 Uncaught (in promise) DOMException: Failed to execute 'postMessage' on 'Worker': xxx could not be cloned.
报错)。
本质上依然是 MessagePort 消息通讯,不过封装了我们所头疼的“操作判断”,并以一种更优雅的方式(Proxy + Promise)来处理。
RPC:Remote Procedure Call,远程过程调用,指调用不同于当前上下文环境的方法,通常可以是不同的线程、域、网络主机,通过提供的接口进行调用。
index.html
import * as Comlink from "https://unpkg.com/comlink/dist/esm/comlink.mjs"
const worker = new Worker("worker.js")
const cw = Comlink.wrap(worker)
comput.addEventListener('click', async function () {
let result = await cw(num1.value, num2.value)
dom.innerHTML = result
})
worker.js
importScripts("https://unpkg.com/comlink/dist/umd/comlink.js")
function add (num1, num2) {
return Number(num1) + Number(num2)
}
Comlink.expose(add)
importScripts()
将一个或多个脚本同步导入到工作者的作用域中。隶属于:WorkerGlobalScope 接口。
扫码关注腾讯云开发者
领取腾讯云代金券
Copyright © 2013 - 2025 Tencent Cloud. All Rights Reserved. 腾讯云 版权所有
深圳市腾讯计算机系统有限公司 ICP备案/许可证号:粤B2-20090059 深公网安备号 44030502008569
腾讯云计算(北京)有限责任公司 京ICP证150476号 | 京ICP备11018762号 | 京公网安备号11010802020287
Copyright © 2013 - 2025 Tencent Cloud.
All Rights Reserved. 腾讯云 版权所有