postMessage的学习总结

994 阅读2分钟

postMessage介绍

Window.postMessage()这个方法主要来解决两个不同页面的脚本跨源通信。

postMessage用法

otherWindow.postMessage(message,targetOrigin,[transfer])
  • message:表示我们需要传递的信息,这个数据会被结构化克隆算法序列化,可以使用JSON.stringify()方法对对象参数序列化。

  • targetOrigin:表示我们需要传数据的窗口(iframe等),这个可以是同源,也可以是不同源。

  • transfer 【可选参数】

    是一串和message 同时传递的 Transferable对象. 这些对象的所有权将被转移给消息的接收方,而发送一方将不再保有所有权。

来个🌰

  • 父页面初始化信息
<h1>This is parent window</h1>
  <input type="text" class="inp">
  <button class="send">发送信息到子页面(child-iframe)</button>
  <div class="contents">
    <p>接收到其他ifram的信息</p>
    <ul class="messages">
    </ul>
  </div>
  <iframe src="http://127.0.0.1:60056/child.html" frameborder="3" class="child-iframe" height="600" width="800"></iframe>
<script>
     // 父页面监听message事件,接受其他iframe发送的消息
    window.addEventListener('message', e => {
      // 我们需要的信息都在这个回调函数中的参数 e 中
     if (e.origin !== 'http://127.0.0.1:8080') { // 验证对方的身份
        return
      }
      const box = document.querySelector('.messages')
      box.innerHTML += `<li> 收到新的信息:${e.data}, 来自于${e.origin}</li>`;
    });

  // 子iframe的实例
   const win = document.querySelector('.child-iframe').contentWindow
    // 绑定我们的点击事件
    document.querySelector('.send').addEventListener('click', () => {
      const msg = document.querySelector('.inp').value
      let msgtest={
          name:123,
          age:20,
          hash:'id=12138&type=search'
      }
      // 第二参数可以使用*,也可以是iframe的URL地址,建议不要使用* ,使用准确的地址
      win.postMessage(msgtest, 'http://127.0.0.1:59726/child.html') 
      document.querySelector('.inp').value = ''
    })
</script>

参数e的属性列表

  • 子页面初始化信息
<h1>This is iframe child page</h1>
  <input type="text" class="inp">
  <button class="send">发送信息到父页面</button>
  <div class="contents">
    <p>接收到的信息</p>
    <ul class="messages">
    </ul>
  </div>
  <script>
    // 监听父页面的消息
    let parentWin=null
    window.addEventListener('message', e => {
       if (e.origin !== 'http://127.0.0.1:60056') { // 验证对方的身份
         return
       }
      // 发送详细窗口的window对象引用,调用对象postMessage方法实现父子页面的通信,当然也可以使用window.parent来通信
      parentWin = e.source
      const box = document.querySelector('.messages')
      box.innerHTML += `<li>接收到新的信息:${e.data}-${e.data.age}, 来自于${e.origin}</li>`
    })
    // 绑定子页面的事件
    document.querySelector('.send').addEventListener('click', () => {
      const msg = document.querySelector('.inp').value
      let msgtest={
          name:123,
          age:20,
          mes:'我是子页面的信息'
      }
      window.parent.postMessage(msgtest, 'http://localhost:8080')
      document.querySelector('.inp').value = ''
    })
  </script>

这两页面分别启动在不同的服务端口下,来模拟跨域场景。

postMessagge优势

  1. 可以实现跨文本档、多窗口、跨域消息传递。
  2. 允许来自不同源的脚本采用异步方式进行有限的通信。

postMessage不足

  1. 当父级页面刷新,message信息就不在了

  2. 传输数据的局限

    1. RegExp 对象的 lastIndex 字段不会被保留

    2. 原形链上的属性也不会被追踪以及复制

    3. 传输需要拿到另一窗口的实例

    4. 数据类型报错DATA_CLONE_ERROR

      • 数据为对象的存在循环引用会报错

      • 对象数据存在function的会报错

      • 传输DOM结点也会报错

传输数据详情: 请点击这个

使用注意

  1. 需要传递消息的窗口需要嵌在父窗口中,
    1. 父传子:需要拿到子页面的window,本文中是使用iframe.contentWindow
    2. 子传父:需要拿到父页面的window,因为子页面嵌入在父页面中,则父页面的window就是子页面中window.parent
  2. 传递时,需要等待子页面加载完成才能发送信息.
  3. 任何窗口可以在任何其他窗口访问此方法,在任何时间,无论文档在窗口中的位置,向其发送消息。 因此,用于接收消息的任何事件监听器必须首先使用origin和source属性来检查消息的发送者的身份
  4. 当发送窗口包含 javascript:data: URL时,origin属性的值是加载URL的脚本的