MessageChannel是JavaScript原生API,需用new MessageChannel()创建配对端口port1/port2,二者绑定即生效、不可混用;端口传输后原作用域失去引用,接收方必须调用start()才能收消息,否则静默丢弃。

MessageChannel 不是 HTML 标签,也不是靠写在 <script></script> 外面或 HTML 属性里就能生效的东西。它必须用 JavaScript 创建、传输、绑定和关闭——哪怕你用在 iframe 或 Worker 里,也得靠代码驱动。
new MessageChannel() 后 port1 和 port2 怎么配对才有效
创建即绑定,不能拆开混用。调用 new MessageChannel() 的瞬间,port1 和 port2 就形成一条私有双向通道:发给 port1 的消息,只有 port2 能收到,反之亦然。
-
port1和port2必须成对使用;把 A 的port1和 B 的port2配对可以,但拿 A 的port1和 C 的port1拼一起,消息永远不通 - 端口一旦被
postMessage(data, targetOrigin, [port])传走,原作用域立刻失去引用,再访问会报"Port is not transferrable" - 主线程中可直接用
port1.onmessage = e => {},但 Worker 或 iframe 中接收方必须显式调用port.start()才能开始收消息(否则静默丢弃,控制台无提示)
iframe 场景下 postMessage 传 port 总失败的常见原因
父页往子 iframe 传 port2 时,90% 的失败不是跨域问题,而是时机或参数错位。
- 必须等
iframe触发load事件后再发 port,提前发——子页还没执行 JS,message事件监听器不存在,port 直接丢失 -
postMessage的第三个参数[port2]不可省略:缺了它,传过去的是空对象或拷贝,不是可通信的 port 实例 - 子 iframe 收到消息后,要检查
e.ports?.length === 1,再取e.ports[0],不能直接解构或假设存在 - 拿到 port 后第一件事是
e.ports[0].start(),之后才能设onmessage或addEventListener('message', ...)
Worker 间直连为什么收不到消息
Worker A 发给 Worker B 的消息没响应?大概率卡在 start() 这一步,而且这个坑不会报错。
立即学习“前端免费学习笔记(深入)”;
- Worker 环境中,
port.onmessage = handler写了也没用,不调port.start()就等于没打开接收开关 - 即使你用
addEventListener('message', ...),也得先port.start(),否则事件根本不会触发 - Worker B 接收连接消息时,必须从
e.ports[0]拿 port,立刻.start(),不能等其他初始化逻辑跑完 - 发送方(Worker A)用
port1.postMessage()不需要start(),但接收方端口未启用 = 白发
微前端中 port 关闭不及时导致内存泄漏
子应用频繁挂载/卸载时,如果 port 没关,监听器残留 + port 持有引用,就会持续吃内存,DevTools 里能看到 detached MessagePort 对象堆积。
- 子应用销毁前必须主动调用
port.close(),不能只移除onmessage监听器 - 主应用收到子应用发来的
{ type: 'DISPOSE' }消息后,也要对本地持有的port1调用.close(),否则 channel 未真正释放 - 关闭后继续
postMessage()会静默失败,可通过port.onmessageerror捕获异常,但更推荐在 close 前就清掉所有定时器和 pending 请求 - 保存 port 引用时避免意外被 GC:比如存在局部变量里又没被闭包持有,GC 可能提前回收 port 导致通信中断
最容易被忽略的一点:MessageChannel 的端口通信是异步宏任务,不保证顺序也不保证原子性;如果你依赖严格时序或需幂等处理,得自己加 id + ack 协议,不能只靠 port 本身。


















