MessageChannel 是 JavaScript API,需用 new MessageChannel() 创建配对端口 port1/port2;端口单次传输、接收方必须调用 start() 才能收消息;跨上下文通信要求同源。
MessageChannel 不是 HTML 标签,也不是靠写在 HTML 文件里就能生效的机制。它是一个 JavaScript API,必须通过 JS 创建和驱动——哪怕你用在 iframe 或 Worker 场景中,也得靠脚本初始化端口、传输、监听。
MessageChannel 实例一创建就生成一对绑定端口:port1 和 port2,它们天然双向、私有、不经过主线程事件循环。new MessageChannel() 创建,不能从 HTML 属性或 DOM 节点上“读出来”port1 和 port2 不能混用到不同通信对中,比如把 A 的 port1 和 B 的 port1 配对,消息不会通postMessage(data, [port]) 传走(进 transfer list),原作用域立刻失去引用,再访问会报 "Port is not transferrable"
const channel = new MessageChannel();const port1 = channel.port1;const port2 = channel.port2;<p>// 比如发给 iframeiframe.contentWindow.postMessage({ type: 'init', port: port2 }, '*', [port2]);
MessageChannel 直连失败,90% 是卡在没调用 start() 或没设 onmessage。port.start() 不是可选的:即使你写了 port.onmessage = e => {...},也得先 port.start() 才能收;否则消息静默丢弃,控制台零提示event.ports[0] 后,必须立即 event.ports[0].start(),不能等其他逻辑执行完port.postMessage() 发送方不需要 start(),但接收方端口未启用 = 白发// Worker B 中self.onmessage = function(e) { if (e.data.type === 'connect' && e.ports?.length) { const remotePort = e.ports[0]; remotePort.start(); // ⚠️ 这行漏掉就永远收不到 remotePort.onmessage = e => console.log('got:', e.data); }};
iframe 加载未完成时就调用 postMessage(..., [port]),会导致端口传输失败或被忽略。iframe 的 contentWindow 可能存在,但其全局执行环境(JS 上下文)还没 ready,此时 postMessage 会静默失败
正确做法:监听 iframe.onload,或在子页面主动用 window.parent.postMessage({ready: true}, ...) 通知父页“我可以收 port 了”
子页面收到 port 后,也要立刻 port.start() 并绑定 onmessage,不能等到 DOM 渲染完再处理
立即学习“前端免费学习笔记(深入)”;
若 iframe 是跨域的,postMessage 本身允许通信,但端口传输仍要求同源(即两个页面脚本必须同源),否则抛 SecurityError
关键点其实就三个:端口只能传一次、接收方必须显式启动、通信双方必须同源才能共享端口。这些限制不是设计缺陷,而是浏览器对跨上下文资源隔离的硬性保障——绕不开,但理清了反而少踩一半坑。