JS-前端跨页面通信指南: postMessage

53 阅读2分钟

前言

在前端开发中,我们经常遇到需要与 iframe 嵌入页面、或者通过 window.open 打开的新窗口进行数据交换的场景。由于浏览器的同源策略限制,普通的脚本访问会被拦截。这时候,window.postMessage 就成了跨源通信的“银弹”。

一、 核心工具:window.postMessage()

postMessage 允许来自不同源的脚本采用异步方式进行有限的通信,可以安全地实现跨源通信。

1. 语法拆解

targetWindow.postMessage(message, targetOrigin, [transfer]);

  • message:将要发送到其他窗口的数据。虽然现代浏览器支持结构化克隆算法(可以传对象),但为了极致的兼容性,有时会手动 JSON.stringify 序列化。

  • targetOrigin (必传) :指定哪些窗口能接收到消息。

    • 安全性警告:如果你不介意谁接收消息,可以设为 "*"。但为了安全,强烈建议指定具体的域名(如 https://example.com),否则数据可能被恶意站点截获。
  • transfer (可选) :用于转移对象的所有权(如 ArrayBuffer),转移后原窗口不再拥有该对象。


二、 接收消息:message 监听事件

接收方需要通过 addEventListener 监听 message 事件。

事件对象关键属性:

  • event.data:收到的消息内容。
  • event.origin最关键的安全属性。发送方的源(协议 + 域名 + 端口)。接收方必须校验此属性,防止接收来自恶意站点的指令。
  • event.source:发送消息的窗口对象的引用。

三、 实战案例:父子窗口通信

1. 父页面(发送方)

假设父页面地址为 https://parent.com

// 获取子窗口的引用(例如 iframe)
const iframe = document.getElementById('childFrame');

// 确保 iframe 加载完成后发送
iframe.onload = function() {
  iframe.contentWindow.postMessage(
    { command: "FETCH_DATA", timestamp: Date.now() }, 
    "https://child.com" // 只有子页面是这个域名时,消息才会发出
  );
};

2. 子页面(接收方)

假设子页面地址为 https://child.com

window.addEventListener("message", (event) => {
  // 【安全检查】必不可少:只接受来自信任域名的消息
  if (event.origin !== "https://parent.com") {
    console.warn("拦截到非法来源的消息:", event.origin);
    return;
  }
  
  // 处理逻辑
  console.log("收到父页面的指令:", event.data.command);
  
  // 回复父页面
  event.source.postMessage("数据已准备好", event.origin);
}, false);

四、 跨页面通信的其他方案(补充)

除了 postMessage,根据是否同源,还有以下常见方案:

通信方式适用场景特点
LocalStorage同源页面监听 storage 事件,适合持久化数据同步。
BroadcastChannel同源页面类似广播,多个标签页订阅同一个频道。
SharedWorker同源页面多个页面共享同一个后台线程。
postMessage跨源 / 同源唯一能突破同源策略限制的官方手段。

五、 避坑指南与总结

  1. 校验 Origin:永远不要在不检查 origin 的情况下处理 message
  2. 避免 * 通配符:发送敏感数据时,务必指定明确的 targetOrigin
  3. 判断窗口是否存在:发送前检查 targetWindow 是否有效。
  4. 数据序列化:虽然现代浏览器支持传对象,