js中【postMessage】八大点解读+完整示例

postMessage() 是 JavaScript 中一个用于在不同的窗口、iframe 或者标签页之间进行跨域通信的 API。它通过一种安全的方式允许文档之间发送消息,而不要求它们在同一个源(域名、协议、端口)上。下面详细讲解 postMessage() 相关的知识点。

1. 基本概念

postMessage() 是 HTML5 引入的 API,允许一个窗口发送消息到另一个窗口,无论它们是否同源。它广泛用于 Web 应用中,尤其是在现代网页应用程序中,用来在主页面和 iframe、弹出窗口、服务端之间进行通信。

2. 语法

window.postMessage(message, targetOrigin, [transfer]);
  • message:要发送的消息,可以是字符串或是任何可序列化的数据(例如对象、数组)。消息内容会通过结构化克隆算法进行传递,类似 JSON 序列化。
  • targetOrigin:指定可以接收消息的窗口的源(协议、域名、端口),以确保消息不会被发送到未知或不信任的站点。可以使用 "*" 表示不限制目标源,但这样可能会带来安全隐患。
  • transfer(可选):传递的对象,通常是像 ArrayBuffer 或者 MessagePort 这样的对象,这些对象会通过转移的方式而不是拷贝传递。

3. 消息接收

要接收通过 postMessage() 发送的消息,目标窗口必须监听 message 事件。可以通过以下方式注册事件处理程序:

window.addEventListener("message", function(event) {
  // 检查消息的来源是否可信
  if (event.origin !== "https://trusted-origin.com") {
    return;
  }

  // 处理接收到的消息
  console.log("Message received:", event.data);
});

4. 安全性

跨域通信中,安全性是最为重要的考虑因素。为了避免消息被不可信的站点截获或伪造,建议遵循以下安全性规则:

  • targetOrigin 的使用:总是显式指定目标源,而不是使用 "*",这样可以确保消息只会发送到特定的站点。

    window.postMessage("Hello", "https://trusted-site.com");
    
  • 验证消息来源:在接收到消息时,检查 event.origin 是否为可信的源,避免处理来自恶意站点的消息。

  • 消息的校验:在处理接收到的消息之前,确保对消息的内容进行校验,防止潜在的恶意代码注入攻击。

5. postMessage() 的应用场景

5.1. iframe 与主页面通信

postMessage() 最常见的使用场景之一是在主页面与嵌入的 iframe 之间进行通信。无论它们是否属于同一个域名,都可以通过 postMessage() 来实现安全通信。

示例:主页面发送消息给 iframe
<!-- 主页面 -->
<iframe id="myFrame" src="https://other-domain.com"></iframe>
<script>
  var iframe = document.getElementById('myFrame');
  iframe.contentWindow.postMessage('Hello iframe', 'https://other-domain.com');
</script>
示例:iframe 接收消息
<!-- iframe 页面 -->
<script>
  window.addEventListener('message', function(event) {
    if (event.origin !== 'https://trusted-domain.com') {
      return;
    }
    console.log('Received message from parent:', event.data);
  });
</script>
5.2. 跨窗口通信

在一个页面中打开了一个新窗口,可以使用 postMessage() 在父窗口和子窗口之间进行通信。

示例:父窗口发送消息给子窗口
var popup = window.open("https://other-domain.com");
popup.postMessage('Hello popup', 'https://other-domain.com');
示例:子窗口接收消息
window.addEventListener('message', function(event) {
  if (event.origin !== 'https://trusted-domain.com') {
    return;
  }
  console.log('Message from parent window:', event.data);
});
5.3. Web Workers 与主线程通信

postMessage() 也可以用于 Web Worker 和主线程之间的通信。由于 Web Worker 没有直接访问 DOM 的权限,它们需要与主线程进行通信来完成任务。

示例:主线程发送消息给 Web Worker
var worker = new Worker('worker.js');
worker.postMessage('Hello Worker');
示例:Web Worker 接收消息
// worker.js
self.addEventListener('message', function(event) {
  console.log('Message from main thread:', event.data);
});
5.4. Service Worker 与页面通信

Service Worker 也是一种场景,页面可以使用 postMessage() 与 Service Worker 通信,实现一些后台任务的交互。

6. event 对象的属性

当目标窗口接收到 message 事件时,event 对象会包含以下属性:

  • data:发送的消息内容。
  • origin:消息来源的协议、域名和端口。
  • source:发送消息的窗口对象,允许我们回复发送方。
  • lastEventId:消息的唯一标识符(通常在服务器发送事件中使用)。
  • ports:如果消息涉及 MessagePort 对象,则可以通过此属性获取传递的端口。

7. 注意事项

  • 跨域限制postMessage() 允许跨域发送消息,但前提是接收方必须显式监听 message 事件,否则消息不会被接收。
  • 消息序列化:消息数据在传递过程中会被序列化(结构化克隆),因此一些复杂对象(如 DOM 元素、函数)无法直接传递。
  • 性能:传递大型对象时,建议使用 transfer 参数以传递而非拷贝数据,从而提高性能。

8. 其他进阶用法

8.1. 传递 MessagePort

postMessage() 支持 MessagePort 传递,可以用于创建双向通信通道。

示例:传递 MessagePort
// 在 iframe 页面中创建一个 MessageChannel
var channel = new MessageChannel();

// 将一端 port 发送给主页面
parent.postMessage('Here is a port', 'https://trusted-domain.com', [channel.port2]);

// 监听消息
channel.port1.onmessage = function(event) {
  console.log('Received message through channel:', event.data);
};

9. 总结

postMessage() 是一个强大而灵活的 API,能够实现不同窗口、iframe、Web Worker 以及其他上下文之间的安全通信。虽然它简化了跨域通信的实现,但也带来了潜在的安全风险。使用 postMessage() 时,必须注意确保消息的发送和接收过程都是可信的,尤其是要谨慎指定 targetOrigin 和验证消息来源的 origin 属性。

10. 完整示例

以下是一个完整的跨窗口通信示例,演示了如何使用 postMessage() 在两个不同的窗口之间传递消息:

<!-- 父页面 -->
<!DOCTYPE html>
<html>
<head>
  <title>Parent Window</title>
</head>
<body>
  <button id="sendMessage">Send Message to Child</button>

  <script>
    var popup = window.open('child.html', 'ChildWindow', 'width=400,height=400');

    document.getElementById('sendMessage').addEventListener('click', function() {
      popup.postMessage('Hello from parent', 'http://example.com');
    });

    window.addEventListener('message', function(event) {
      if (event.origin !== 'http://example.com') return;
      console.log('Received message from child:', event.data);
    });
  </script>
</body>
</html>
<!-- 子页面 (child.html) -->
<!DOCTYPE html>
<html>
<head>
  <title>Child Window</title>
</head>
<body>
  <script>
    window.addEventListener('message', function(event) {
      if (event.origin !== 'http://example.com') return;
      console.log('Received message from parent:', event.data);
      event.source.postMessage('Hello from child', event.origin);
    });
  </script>
</body>
</html>

这个示例展示了父窗口和子窗口如何通过 postMessage() 进行双向通信,并通过 event.origin 验证消息来源的安全性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值