MessageChannel 不能用于不同 iframe 沙箱间通信,仅支持同源同执行上下文(如主线程↔Worker);跨 iframe 必须用 postMessage,因其是唯一原生支持且安全的跨文档通信机制。
MessageChannel 不能直接用于不同 iframe 沙箱间的通信——它只适用于同源、同执行上下文(如主线程 ↔ Worker,或同一 iframe 内部的两个 script 标签)之间的通道通信。跨 iframe(尤其是跨域或带 sandbox 属性的 iframe)必须走 postMessage,这是唯一被浏览器原生支持且安全的跨文档通信机制。
MessageChannel 创建的是一个双向、低延迟的“管道”,但它的两个 port 对象必须由同一方创建并显式传递给另一方(例如通过 worker.postMessage(ports))。而不同 iframe 是完全独立的 JS 上下文,无法共享内存或对象引用:iframeA.contentWindow 拿不到 iframeB 的任何 port 实例,也无法把 port 序列化后传过去(port 不可克隆、不可传输到非所属上下文)。
new MessageChannel(),然后 iframeB.contentWindow.postMessage(port1, '*') → 会报错:DataCloneError: An object could not be cloned
iframeA 和 iframeB 的 window 对象互不可写、不可传对象,仅能传可序列化的值(字符串、普通对象、ArrayBuffer 等)MessageChannel 的核心价值在于避免频繁 postMessage 的序列化开销,但它依赖“对象直传”,这在跨 iframe 场景中根本不存在所有 iframe 沙箱间通信的底层都只能是 postMessage,但你可以用轻量协议提升效率和可靠性,替代裸用 postMessage:
{ type: 'handshake', id: 'iframe-a' },父窗口记录其 event.source(即该 iframe 的 contentWindow 引用)id 转发给 B:targetIframe.contentWindow.postMessage(msg, targetOrigin)
event.origin 校验来源(哪怕同域也别用 '*')event.source.postMessage(reply, event.origin) 回传,父窗口再转回 A —— 这就是标准的双向通信链路如果你的 iframe 带了 sandbox 属性,postMessage 是否还能用?答案是:能,但必须显式开启 allow-scripts,否则脚本根本不会执行,更别说监听 message 事件了:
<iframe sandbox="allow-scripts" src="child.html"></iframe> ✅ 允许执行脚本,可收发 postMessage
<iframe sandbox="" src="child.html"></iframe> ❌ 所有脚本禁用,addEventListener('message', ...) 不会运行allow-same-origin 不是必须的:跨域通信本身不依赖同源,postMessage 天然支持跨域;但加了它后,event.origin 可能变成实际域名而非 'null',便于校验allow-top-navigation 或 allow-popups——与通信无关,反而扩大攻击面多数人以为只要父子通信通了,iframe 之间就能“自动连通”。实际上,父窗口必须主动维护一份 iframe 映射表,否则无法路由消息。更隐蔽的问题是:如果某个 iframe 因异常重载或销毁,其 contentWindow 引用就失效了,继续往它 postMessage 会静默失败(无报错、无回调)。因此,生产环境必须:
load 事件,动态更新引用iframe.contentWindow && !iframe.contentWindow.closed
{ type: 'call', id: 'req-123' },B 必须回 { type: 'ack', id: 'req-123' })