MessageChannel仅用于安全高效传递数据,不支持跨域DOM直接操作;需父页面分发端口,发送端节流防洪峰,接收端防抖保活,配合心跳与错误处理实现链路自愈。
MessageChannel 不是用来“直接操作对方 DOM”的工具,它只负责安全、高效地传递数据。两个独立 iframe 之间无法互相访问对方的 DOM(跨域时连 window 对象都拿不到),所谓“DOM 通讯”实际是指:一方 DOM 状态变化后,将变更信息通过 MessageChannel 同步给另一方,由对方根据消息自行更新本地 DOM。高频场景下关键不在“传得快”,而在“不积压、不丢帧、不阻塞、可恢复”。
明确通信角色与通道归属
两个 iframe(比如 A 和 B)是平级关系,没有父子嵌套,也不能直接获取彼此的
或
。必须引入第三方协调者——通常是它们共同的父页面(parent.html)。该父页不参与业务逻辑,只做端口分发信使:
父页创建
,得到
和
用
把
发给 iframe A
用
把
发给 iframe B
A 和 B 各自收到 port 后立即调用
,再绑定
发送端:控制节奏,避免突发洪峰
高频 DOM 变化(如拖拽位置、滚动偏移、实时表单校验)若逐帧发消息,极易压垮接收方事件循环。需在发送侧主动节流:
对连续值(如
)采用“差量更新”:只发变化字段,而非全量对象
使用
或
将 postMessage 推入微任务队列末尾,避开主线程高峰
设置最小发送间隔(例如 16ms),用时间戳 + 标志位防重复触发
禁止在
、
等高频回调里直接
接收端:防抖 + 保活 + 容错更新
接收到消息后不能无脑操作 DOM,尤其要防止因 JS 执行阻塞导致后续消息堆积:
对同类型高频消息(如坐标更新)启用“最后帧优先”策略:100ms 内收到多条,只保留最新一条,丢弃中间帧
更新 DOM 前检查
是否仍处于
,避免向已关闭端口发消息
关键 DOM 操作(如重绘表格、切换 tab)包裹在
中,确保与浏览器刷新节奏同步
监听
,捕获序列化失败等异常;收到非法结构消息时静默忽略,不 throw
状态监控与链路自愈
iframe 可能被动态移除、reload 或崩溃,通信链路需具备轻量级健康感知能力:
A 和 B 启动后互发带
的心跳消息(如
)
任一方 300ms 未收到对方
回复,主动
并触发重连流程(再次向父页请求新 port)
父页监听 iframe 的
和
事件,检测子页是否异常重启,并按需补发 channel 初始化消息
所有消息建议加简单类型标识(
)和版本号(
),便于未来协议演进兼容
windowcontentWindownew MessageChannel()port1port2postMessage({ type: 'init-channel', port: port1 }, targetOrigin)port1postMessage({ type: 'init-channel', port: port2 }, targetOrigin)port2port.start()onmessage{ x: 102, y: 45 }requestIdleCallbacksetTimeout(fn, 0)oninputonscrollport.postMessage()portport.readyState === 'open'requestAnimationFrameport.onmessageerrortimestamp{ type: 'ping', ts: Date.now() }pongport.close()loaderrortype: 'dom-update:scroll'v: 2