使用window对象传递消息

3,075 阅读4分钟

window对象传递消息

随着前端业务逻辑的愈发复杂化,常需要在不同窗口之间进行数据通信,本文介绍了如何使用message事件进行数据通信的行为。

1. window.top对象

window.top是在Web浏览器环境中使用的JavaScript全局对象,它提供了对顶级窗口的引用。顶级窗口是当前浏览器标签页或窗口层次结构中的最高级别窗口。

以下是一些关于 window.top 的重要信息:

  • window.top 提供了对顶级窗口的引用,通常是指当前浏览器标签页的最顶层窗口对象。这个顶级窗口对象可以访问和操作整个窗口层次结构,包括其他嵌套框架或窗口。
  • 通过 window.top,你可以访问顶级窗口的属性和方法,例如 window.top.document 表示顶级窗口的文档对象,window.top.location 表示顶级窗口的URL地址等。
  • 使用 window.top 可以方便地在嵌套的框架或窗口之间进行通信和操作。通过 window.top 对象,可以向上遍历窗口层次结构,与其他同级或父级窗口进行交互。
  • 需要注意的是,由于安全性限制,跨域的窗口访问可能会受到限制。如果脚本位于不同的域名或协议下,访问 window.top 的属性和方法可能会受到同源策略的限制

总之,window.top 是一个指向顶级窗口对象的引用,它提供了在Web浏览器环境中访问和操作顶级窗口的功能,用于处理窗口间的通信和操作

2. window对象和消息机制

  • iframe标签对象上有一个名为contentWindow的对象,这个对象存在的价值为:当A.html中使用ifame引用b.html的时候,b文档对应的contentWindow对象实际上提供了a和b进行通信的接口;
  • contentWindow对象是b文档在a文档中被引用的时候的接口对象,记为contentWindow::BinA,对应的是b文档本身的window对象,记为window::BinB,数据流从a到b的过程,实际上是从contentWindow::BinAwindow::BinB的过程;
  • 也就是contentWindow::BinA发消息,而window::BinB接受消息:
// a.html
contentWindow.postMessage('msg from a', '*')
// b.html
window.addEventListener('message', function(e){
    console.log(e.data);
})

3. contentWindow对象的获取

强调一下:contentWindow对象是iframe标签对象的一个属性:

const contentWindow = document.getElementById("bFrame").contentWindow;

4. iframe的name属性

在iframe标签上设置name属性,可以方便在通信过程中识别数据源,如下:

<iframe src="b.html" id="bFrame" name="bFrame" style="width:100%;height:600px;"></iframe>

5. message事件的事件对象

window使用addEventListener监听message事件,对应的事件对象e中有两个常用的属性:

  • data: 其值为传递的数据
  • source: 表示数据源,数据源的类型是Window

6. 实例

6.1 项目结构

mkdir test-window && cd test-window
touch index.html parent.html child.html

6.2 index.html中的内容

<!DOCTYPE html>
<html>
  <head>
    <title>Communication Example</title>
    <script>
      function sendMessageToParent() {
        // 1. 找到文档对应的接口对象contentWindow
        var parentFrame = document.getElementById("parentFrame").contentWindow;
        // 2. 使用contentWindow上的postMessage方法传递信息
        parentFrame.postMessage("Hello from top Window!", "*");
      }
    </script>
  </head>
  <body>
    <h1>Communication Example</h1>
    <!-- 点击按钮之后获取parent.html对应的window对象,使用这个对象上的postMessage向parent.html传递消息
    也就是说,contentWindow是内嵌html提供的通信接口 -->
    <button onclick="sendMessageToParent()">Send Message to Parent</button>

    <iframe src="parent.html" id="parentFrame" name="parentFrame" style="width:100%;height:600px;"></iframe>
  </body>
</html>

6.3 parent.html中的内容

<!DOCTYPE html>
<html>
  <head>
    <title>Parent Window</title>
  </head>
  <body>
    <h1>Parent Window</h1>

    <iframe src="child.html" id="childFrame" name="childFrame" style="width:100%;height:400px;"></iframe>

    <script>
      // 1. 本文档的window对应的就是在其它文档中标签对象上的contentWindow属性
      // 2. 使用window监听message事件,即可订阅从其他文档中通过postMessage方法传递过来的信息
      window.addEventListener("message", function(event) {
        if (event.source === window.top) {
          console.log("Message received from Top Window:", event.data);
          // 3. 将收到的消息传递给下一层文档
          transformMessageToChild(event.data);
        }
      });
      
      // 4. 同样是先获取contentWindow,然后使用其传递消息
      function transformMessageToChild(data) {
        var childFrame = document.getElementById("childFrame").contentWindow;
        childFrame.postMessage(`(msg from top, delivered by parent) ${data}`, "*");
      }
    </script>
  </body>
</html>

6.4 child.html

<!DOCTYPE html>
<html>
  <head>
    <title>Child Frame</title>
  </head>
  <body>
    <h1>Child Frame</h1>

    <script>
      window.addEventListener("message", function(event) {
        if (event.source.name === "parentFrame") {
          console.log("Message received from Parent Window:", event.data);
        }
      });
    </script>
  </body>
</html>

效果

点击按钮之后,控制台打印的内容:

Message received from Top Window: Hello from top Window!                                            parent.html:26 
Message received from Parent Window: (msg from top, delivered by parent) Hello from top Window!     child.html:12

7. 补充:postMessage的第二个参数

  • postMessage() 方法中,第二个参数(targetOrigin)指定了目标窗口的来源。

  • '*' 作为 targetOrigin 参数传递给 postMessage() 方法时,它表示可以接受来自任何源(包括不同的域、协议和端口)发送的消息。具体来说,'*' 是一个通配符,用于指示无论来源是什么,都可以接收消息。这意味着消息会被发送到目标窗口的所有窗口对象(即 window 对象)。

  • 在真实的生产环境中,使用 '*' 应谨慎。因为它允许接受来自任何来源的消息,可能存在安全风险。为了确保安全性,最好将 targetOrigin 参数设置为确切的值,以限制只接受来自特定源的消息。

  • 例如,如果你的目标窗口是位于 https://example.com 域名下的页面,你可以将 targetOrigin 设置为该域名:

childFrame.postMessage("Hello from Parent Window!", "https://example.com");

这样,只有来自 https://example.com 域名的窗口发送的消息才会被接受。其他来源的消息将被忽略,从而提供了更严格的安全性。