浏览器
Iframe 通信
跨源的父页面与 iframe 之间无法直接读取对方的 DOM 或变量,需要通过 window.postMessage 传递消息。
postMessage API
targetWindow.postMessage(data, targetOrigin)| 参数 | 说明 |
|---|---|
data | 发送的数据,会被结构化克隆,支持对象、数组等 |
targetOrigin | 接收方的源(协议 + 域名 + 端口),指定 '*' 表示不限来源 |
接收方通过监听 message 事件获取消息:
| 事件属性 | 说明 |
|---|---|
event.data | 接收到的数据 |
event.origin | 发送方的源 |
event.source | 发送方的 window 对象引用,可用于回复 |
始终校验 event.origin,忽略来源不可信的消息,否则页面上任意第三方 iframe 都可以向你发消息。
父页面 → iframe
// 父页面:向 iframe 发送消息
const iframe = document.querySelector('iframe')
iframe.contentWindow.postMessage(
{ type: 'greeting', text: 'hello' },
'https://child.example.com', // iframe 的源
)// iframe:接收父页面消息
window.addEventListener('message', (event) => {
if (event.origin !== 'https://parent.example.com') return // 校验来源
console.log(event.data) // { type: 'greeting', text: 'hello' }
})iframe → 父页面
// iframe:向父页面发送消息
window.parent.postMessage(
{ type: 'response', text: 'hello parent' },
'https://parent.example.com', // 父页面的源
)// 父页面:接收 iframe 消息
window.addEventListener('message', (event) => {
if (event.origin !== 'https://child.example.com') return // 校验来源
console.log(event.data) // { type: 'response', text: 'hello parent' }
})利用 event.source 回复消息
接收到消息后,可以通过 event.source 直接向发送方回复,无需提前持有对方的 window 引用:
// iframe:收到消息后回复父页面
window.addEventListener('message', (event) => {
if (event.origin !== 'https://parent.example.com') return
// event.source 是父页面的 window
event.source.postMessage(
{ type: 'ack', received: true },
event.origin, // 用 event.origin 作为 targetOrigin,安全且准确
)
})安全注意事项
不要使用 targetOrigin: '*',除非明确不在乎消息被任何页面收到。如果页面被恶意 iframe 嵌套,* 会导致敏感数据泄露给攻击者。
// ❌ 不安全:任何页面都能收到消息
iframe.contentWindow.postMessage(sensitiveData, '*')
// ✅ 安全:只有指定源的页面才能收到
iframe.contentWindow.postMessage(sensitiveData, 'https://child.example.com')接收方同样需要过滤来源:
window.addEventListener('message', (event) => {
// ❌ 不校验来源,任何页面发来的消息都会处理
handleMessage(event.data)
// ✅ 只处理可信来源的消息
if (event.origin !== 'https://trusted.example.com') return
handleMessage(event.data)
})