跨域 iframe 通信必须用 postMessage,发送前需确保 iframe 加载完成并指定精确目标源;接收方须严格校验 Event.origin,响应需带唯一 ID 并实现超时控制。

iframe.contentwindow.postMessage 发送跨域消息
直接访问跨域 iframe 的 window 或 document 会触发安全错误,必须用 postMessage。发送方调用 iframe.contentWindow.postMessage(),第一个参数是任意可序列化的数据,第二个参数是目标源(不能写 *,必须明确协议+域名+端口)。
-
iframe必须已加载完成(监听load事件后再发),否则contentWindow可能为NULL - 目标源写错(比如漏了
https://、端口不匹配、用了*)会导致消息静默丢弃,控制台无报错 - 若 iframe 来自同域,也可用
postMessage,但此时接收方可省略源校验(不推荐)
const iframe = document.getElementById('myIframe'); iframe.addEventListener('load', () => { iframe.contentWindow.postMessage({ type: 'INIT', data: 'hello' }, 'https://remote.example.com'); });
监听 message 事件并校验 origin
接收方在 window 上监听 message,关键在验证 event.origin —— 这是浏览器自动注入的发送源,不可伪造。切勿只校验 event.source 或信任 event.data 里的字段。
-
event.origin格式严格为https://domain.com:port,不含路径;本地文件用file://时值为null(需特殊处理) - 多个 iframe 可能同时发消息,建议用
event.source === iframe.contentWindow做双向绑定校验(尤其当页面含多个跨域 iframe) - 避免直接执行
event.data中的函数字符串或eval,应只解析结构化数据
window.addEventListener('message', (event) => { if (event.origin !== 'https://remote.example.com') return; if (event.data.type === 'INIT') { console.log(event.data.data); // 'hello' } });
postMessage 回调与响应机制怎么设计
原生 postMessage 是单向的,如需响应,双方需约定唯一 id 字段 + 回调标识。常见做法:发送方生成随机 id,接收方收到后用同一 id 回复到 event.source。
-
event.source指向发送方的window对象,可直接用于回复,无需再存引用 - 不要依赖
event.ports(仅用于channel Messaging,且跨域 iframe 不支持) - 超时控制必须由应用层实现:发送方设
setTimeout,接收方响应时清除定时器
// 发送方(父页) const msgId = Date.now() + '-' + Math.random().toString(36).substr(2, 9); const timeout = setTimeout(() => console.error('timeout'), 5000); window.addEventListener('message', function onReply(event) { if (event.data.id === msgId && event.origin === 'https://remote.example.com') { clearTimeout(timeout); window.removeEventListener('message', onReply); console.log('reply:', event.data.payload); } }); iframe.contentWindow.postMessage({ id: msgId, type: 'GET_DATA' }, 'https://remote.example.com');
iframe 加载失败或未就绪时的容错处理
跨域 iframe 加载失败(如 404、CORS 阻止、网络中断)时,load 事件不触发,contentWindow 可能为 null 或抛出跨域异常。无法通过 onerror 捕获 iframe 资源错误(浏览器限制)。
立即学习“Java免费学习笔记(深入)”;
- 设置
iframe的src后,立即检查iframe.contentWindow是否存在;若为null,说明尚未加载或被拦截 - 可用
iframe.onload和iframe.onerror组合判断:虽然onerror对跨域资源不总触发,但对同域 fallback 页面有效 - 更可靠的方式是让子页面主动通知父页“已就绪”——子页加载完立即
postMessage({ type: 'READY' }),父页监听并设状态标志
跨域通信不是“连上就通”,而是“每次消息都要校验、每次响应都要防丢”。最常被忽略的是源校验的严格性和超时兜底——没有这两条,线上出问题时几乎无法定位。