前言
postMessage
和 MessageChannel
都是用来实现跨文档、跨窗口或跨线程(Web Worker)的消息传递机制。
postMessage
可以在 iframe、同源或跨源窗口之间传递数据,也可以用于主线程与 Web Worker 之间的通信。
postMessage
是一种单向的消息传递机制,适用于同源或跨域窗口之间的通信。可以用它来向一个窗口发送消息,而这个窗口通过监听 message 事件来接收消息。通常用于主页面和 iframe 或主线程和 Web Worker 之间的通信。
// 在主线程中创建 Web Worker
const worker = new Worker('worker.js');// 发送消息到 Worker
worker.postMessage({ type: 'INIT', data: 'Hello!' });// 在 Worker 中监听消息
self.addEventListener('message', (event) => {console.log('监听到的消息:', event.data);// 发送响应回主线程self.postMessage('你好!');
});// 在主线程中监听 Worker 的响应
worker.addEventListener('message', (event) => {console.log('监听到的消息:', event.data);
});
postMessage
实现iframe
跨域通信请看本人另一篇文章:postMessage实现iframe跨域通信
MessageChannel
MessageChannel
创建两个 MessagePort
对象,这两个端口可以通过 postMessage
相互通信。它比 postMessage
更灵活,适用于需要复杂双向通信的场景。
// 创建一个 MessageChannel 实例
const channel = new MessageChannel();// 获取两个端口
const port1 = channel.port1;
const port2 = channel.port2;// 监听 port1 上的消息
port1.onmessage = (event) => {console.log('接收到的消息:', event.data);
};// 向 port2 发送消息
port2.postMessage('port2发送的消息');
可以在不同的上下文中使用 postMessage 传递 port2 或 port1
file1.js
const channel = new MessageChannel();// 监听来自 port1 的消息
channel.port1.onmessage = (event) => {console.log("接收到的消息:", event.data);
};// 将 port2 传递给目标文件(iframe 或 Web Worker)
const iframe = document.querySelector('iframe');
iframe.contentWindow.postMessage('Sending port', '*', [channel.port2]);// 向 port1 发送一条消息
channel.port1.postMessage("你好,我是file1的消息");
file2.js
// 监听来自主页面的消息
window.addEventListener('message', (event) => {// 检查是否收到了 MessageChannel 的 portif (event.data === 'Sending port' && event.ports.length > 0) {const port2 = event.ports[0];// 监听 port2 上的消息port2.onmessage = (event) => {console.log("监听到的消息:", event.data);// 回应 port1 的消息port2.postMessage("我是file2的消息");};}
});
React 调度器 Scheduler 源码也有用到 MessageChannel
,根据可用的 API 选择最合适的调度方法。
- Node.js 环境中,优先使用
setImmediate
,它的执行时间更早,并且不会阻止 Node.js 进程退出。 MessageChannel
有时可能导致 Node.js 进程延迟退出。在浏览器环境中,比setTimeout
更精确地控制任务执行时间(更少的时间颗粒度)。例如,setTimeout 可能会有最低延迟限制(如 4 毫秒),而 MessageChannel 可以更快地触发任务。localSetTimeout
: 标准的setTimeout
函数,如果在环境中既没有setImmediate
也没有MessageChannel
,那么使用setTimeout
作为最后的回退方案。通常是在非浏览器环境中,比如一些测试环境或非常旧的环境中。
总结
postMessage | MessageChannel | |
---|---|---|
是否支持跨域 | 是 | 是 |
通信方式 | 单向通信 | 双向通信 |
消息数据 | 可以传递基本数据类型和结构化克隆的数据(如对象、数组、文件等),但不能直接传递上下文(如函数) | 不仅可以传递数据,还可以通过 postMessage 传递 MessagePort,从而创建多个通信通道 |
优势 | 简单快捷 | 适用于较复杂的双向通信 |