跨域 iframe 通信必须用 postMessage,发送前需确保 iframe 加载完成并指定精确目标源;接收方须严格校验 event.origin,响应需带唯一 ID 并实现超时控制。
直接访问跨域 iframe 的 window 或 document 会触发安全错误,必须用 postMessage。发送方调用 iframe.contentWindow.postMessage(),第一个参数是任意可序列化的数据,第二个参数是目标源(不能写 *,必须明确协议+域名+端口)。
iframe 必须已加载完成(监听 load 事件后再发),否则 contentWindow 可能为 null
https://、端口不匹配、用了 *)会导致消息静默丢弃,控制台无报错postMessage,但此时接收方可省略源校验(不推荐)const iframe = document.getElementById('myIframe');
iframe.addEventListener('load', () => {
iframe.contentWindow.postMessage({ type: 'INIT', data: 'hello' }, 'https://remote.example.com');
});
接收方在 window 上监听 message,关键在验证 event.origin —— 这是浏览器自动注入的发送源,不可伪造。切勿只校验 event.source 或信任 event.data 里的字段。
event.origin 格式严格为 https://domain.com:port,不含路径;本地文件用 file:// 时值为 null(需特殊处理)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 是单向的,如需响应,双方需约定唯一 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 加载失败(如 404、CORS 阻止、网络中断)时,load 事件不触发,contentWindow 可能为 null 或抛出跨域异常。无法通过 onerror 捕获 iframe 资源错误(浏览器限制)。
iframe 的 src 后,立即检查 iframe.contentWindow 是否存在;若为 null,说明尚未加载或被拦截iframe.onload 和 iframe.onerror 组合判断:虽然 onerror 对跨域资源不总触发,但对同域 fallback 页面有效postMessage({ type: 'READY' }),父页监听并设状态标志跨域通信不是“连上就通”,而是“每次消息都要校验、每次响应都要防丢”。最常被忽略的是源校验的严格性和超时兜底——没有这两条,线上出问题时几乎无法定位。