postMessage 是 HTML5 提供的一种跨窗口通信机制,通过安全的消息传递方式突破浏览器的同源策略限制,实现不同源窗口之间的数据交互。以下是其解决跨域问题的原理及实际应用方法:
一、postMessage 如何解决跨域问题?
- 绕过同源策略限制
postMessage 允许不同协议、域名或端口的窗口(如 iframe、弹出窗口、标签页等)通过消息事件进行通信,而无需直接访问对方的 DOM 或数据。 - 安全的通信机制
- 发送端:通过 targetOrigin 参数指定接收窗口的源(协议+域名+端口),只有匹配该值的窗口才能接收到消息,避免消息被恶意截获。
- 接收端:通过校验 event.origin 属性过滤非预期的消息来源,确保仅处理可信源的请求28。
- 结构化数据传递
支持传输字符串、对象、数组等结构化数据(通过序列化算法处理),甚至可以传递 ArrayBuffer 等二进制数据(需结合 transfer 参数)。
二、postMessage 的核心使用步骤
- 发送消息
在发送端窗口中调用 postMessage 方法:
// 假设 otherWindow 是目标窗口的引用(如 iframe.contentWindow)
otherWindow.postMessage(message, targetOrigin, [transfer]);
- message:要传递的数据(如字符串、对象)。
- targetOrigin:接收窗口的源(如 "https://domain-b.com"),或 "*" 表示任意源(不推荐)
2.接收消息
在接收端监听 message 事件,并校验来源:
window.addEventListener("message", (event) => {
if (event.origin !== "https://expected-origin.com") return; // 安全校验
console.log("Received:", event.data);
// 可选:向发送端回复消息
event.source.postMessage("Reply", event.origin);
});
- event.data:接收到的数据。
- event.source:发送窗口的引用,可用于回信。
三、工作中的常见应用场景
- iframe 与父页面通信
- 场景:父页面调整 iframe 高度、传递配置参数。
- 示例:
<!-- 父页面 -->
<iframe id="child" src="https://child-domain.com"></iframe>
<script>
const iframe = document.getElementById("child");
iframe.contentWindow.postMessage({ type: "resize", height: 500 }, "https://child-domain.com");
</script>
<!-- 子页面 -->
<script>
window.addEventListener("message", (e) => {
if (e.origin !== "https://parent-domain.com") return;
if (e.data.type === "resize") document.body.style.height = `${e.data.height}px`;
});
</script>
跨域数据共享
- 场景:不同域的页面间同步用户登录状态或共享数据。
- 示例:主页面通过 window.open 打开子窗口,子窗口通过 postMessage 返回用户授权信息
Web Worker 通信
- 场景:主线程与 Web Worker 传递计算任务或结果。
- 示例:
// 主线程
const worker = new Worker("worker.js");
worker.postMessage({ task: "processData", data: largeArray });
// Worker 线程
self.addEventListener("message", (e) => {
const result = heavyProcessing(e.data);
self.postMessage(result);
});
- 严格校验来源
始终在接收端验证 event.origin,避免处理来自未知源的消息,防止 XSS 攻击。 - 避免使用通配符 *
发送消息时尽量指定具体的 targetOrigin,而非 "*",以减少安全风险。 - 敏感数据加密
若传递敏感信息(如 Token),建议结合加密算法(如 AES)确保数据安全。