核心是明确事件流向、隔离作用域、可控触发时机;父子窗口通信必须通过 window.postMessage 发送结构化数据,并用 addEventListener('message') 监听,严格校验 origin 和 source,按 type 分发处理。

用 addEventListener 实现父子窗口通信,核心不是“加监听”,而是**明确事件流向、隔离作用域、可控触发时机**。自研大屏组件常嵌在 iframe 或子窗口中,父子间默认无直接 DOM 关系,不能靠冒泡穿透,必须走 window.postMessage + addEventListener('message') 这条正路。
一、父窗口发消息:用 postMessage 发送结构化数据
父窗口(主屏)向子窗口(如仪表盘 iframe)通信,不能直接调子窗的函数或改它的变量——跨源限制会报错。必须用 postMessage:
- 确保拿到子窗口引用(
iframe.contentWindow或window.open()返回值) - 消息体必须是可序列化的对象,别传函数、DOM 节点、undefined
- 第二个参数写对子窗 origin(如
'https://dash.example.com'),开发期可用'*',上线前必须收窄
示例:
// 父窗口
const iframe = document.getElementById('dashboard');
const childWin = iframe.contentWindow;
childWin.postMessage(
{ type: 'SET_FILTER', payload: { region: 'east', year: 2024 } },
'https://dash.example.com'
);
二、子窗口监听 message:用 addEventListener 绑定一次,不重复注册
子窗口里监听 message 事件,是通信链路的“入口”。常见错误是每次初始化都 addEventListener 却不 removeEventListener,导致多次响应同一消息。
- 监听逻辑应放在子窗 JS 入口处(如
main.js最外层),只执行一次 - 必须校验
event.origin,防止恶意站点伪造消息 - 用
event.source缓存父窗引用,后续回传时复用
示例:
// 子窗口
let parentRef = null;
window.addEventListener('message', (e) => {
if (e.origin !== 'https://mainboard.example.com') return;
parentRef = e.source; // 记住父窗,用于后续 reply
const { type, payload } = e.data;
switch (type) {
case 'SET_FILTER':
applyFilter(payload);
break;
case 'REFRESH_DATA':
loadData();
break;
}
});
三、子窗口回传:用 event.source 或缓存的 parentRef 发消息
子窗处理完逻辑后要通知父窗(比如“筛选已生效”或“数据加载完成”),不能调父窗函数,只能再走 postMessage。关键点:
- 优先用收到消息时的
e.source,它天然可信且指向发信方 - 若需异步回传(如请求完成后),就把
e.source存到闭包或模块级变量(如上面的parentRef) - 回传消息也建议带
type字段,方便父窗做类型分发
示例:
// 子窗口内某异步操作后
function notifyParentReady() {
parentRef?.postMessage(
{ type: 'READY', payload: { timestamp: Date.now() } },
'https://mainboard.example.com'
);
}
四、父窗口监听子窗回传:同样校验 origin,按 type 分发
父窗也要监听 message,接收子窗反馈。注意:
- 监听代码放在父窗初始化阶段,和发消息逻辑解耦
- 通过
e.source === iframe.contentWindow可进一步确认来源是目标 iframe(非其他 postMessage) - 避免在监听回调里直接操作子窗 DOM(仍受跨源限制),所有交互都靠消息驱动
示例:
// 父窗口
window.addEventListener('message', (e) => {
if (e.origin !== 'https://dash.example.com') return;
if (e.source !== iframe.contentWindow) return; // 精确匹配
const { type, payload } = e.data;
if (type === 'READY') {
console.log('子窗就绪,可下发指令');
} else if (type === 'DATA_UPDATED') {
updateUIStatus(payload);
}
});
整条链路清晰了:父 → postMessage → 子(监听 message → 处理 → 可选 reply)→ 父(监听 message → 响应)。没有 DOM 直接调用,不依赖全局变量,不污染 window,完全符合同源策略,也经得起 iframe 沙箱化或跨域部署考验。

















