postMessage

5,704 阅读3分钟

1、什么是postMessage

postMessage是HTML5中新引入的API,它可以实现跨窗口以及跨域的通信。postMessage类似与Ajax但是它不受同源策略的限制并且通信双方都是客户端。

2、postMessage相关API介绍

2.1、发送数据

语法:

quoteWindow.postmessage(data, origin, [transfer])

quoteWindow

其中quoteWindow表示其他窗口的一个引用,是被发送数据的一方(接收数据),可以是以下类型

  1. iframe的contentWindow
<body>
    <iframe class="childIframe" scr="http://XXX:8080"></iframe>
    <script>
        // 获取iframe的contentWindow
        const win = document.querySelector('.childIframe').contentWindow
    </script>
</body>
  1. 执行window.open()方法返回的对象
<body>
    <script>
        // 获取window.open()打开窗口的引用
        const win = window.open('http://XXX:8888')
    </script>
</body>
  1. 命名过或数值索引的window.frames
<body>
    <script>
        const iframes = window.frames
    </script>
</body>

data

data表示发送的数据,在MDN中说到:data表示将要发送到其他 window的数据。它将会被结构化克隆算法序列化。这意味着你可以不受什么限制的将数据对象安全的传送给目标窗口而无需自己序列化。[1]

origin

通过origin可以指定哪些窗口可以接收到该消息事件,它的值可以是以下:

  • "*"

表示不受限制

  • URL地址

表示只有该URL地址的窗口才能接收到消息。这个设计尤为重要,因为我们在传输一些敏感信息的时候就需要指定窗口才能接收该事件,防止恶意攻击。

transfer

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

总结一下

发送数据使用postMessage方法,要那个窗口发送数据就调用该窗口的postMessage的方法,并在方法中传入相关的参数。

2.2、接收数据

语法

window.addEventListener('message', (e) => {
    // ...
})

在需要接收消息的页面中监听一个**message**事件,当其他窗口发送数据后就会触发该事件,然后执行相应的事件函数。

在接收到的事件对象中有三个重要的属性

  1. origin;表示发送消息窗口的源,可以通过此属性判断源是否安全
  2. data;表示发送消息窗口发送的数据
  3. source;表示对发送消息窗口的引用,可以用此来向发送消息的窗口返回数据

3、具体使用

定义父窗口:

<body>
  <h1>This is parent window</h1>
  <input type="text" class="inp">
  <button class="send">发送信息到iframe</button>
  <div class="contents">
    <p>接收到的信息</p>
    <ul class="messages">

    </ul>
  </div>
  <iframe src="child.html" frameborder="3" class="child-iframe" height="600" width="800"></iframe>
  <script>
    // 父页面监听message事件,接受iframe发送的消息
    window.addEventListener('message', e => {
      if (e.origin !== 'http://127.0.0.1:5500') { // 验证对方的身份
        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
      win.postMessage(msg, '*') // 这里使用*,也可以是iframe的URL地址
      document.querySelector('.inp').value = ''
    })
  </script>
</body>

定义子窗口

<body>
  <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
    // 监听父页面的消息
    window.addEventListener('message', e => {
      if (e.origin !== 'http://127.0.0.1:5500') { // 验证对方的身份
        return
      }
      // 发送详细窗口的window对象引用,调用对象postMessage方法实现父子页面的通信,当然也可以使用window.parent来通信
      parentWin = e.source
      const box = document.querySelector('.messages')
      box.innerHTML += `<li>接收到新的信息:${e.data}, 来自于${e.origin}</li>`
    })
    console.log(window.parent.location)

    document.querySelector('.send').addEventListener('click', () => {
      const msg = document.querySelector('.inp').value
      window.parent.postMessage(msg, '*') // 
      document.querySelector('.inp').value = ''
    })
  </script>
</body>

这样就实现了父子窗口之间的通信。如下图:

postMessage还可以实现跨域通信等,后续继续研究。

4、修改iframe中DOM节点的样式

4.1、获取到元素后修改

window.onload = function () {
    let dom = document.getElementById('frame').contentWindow.document.getElementById('selector')
    // 修改style
    dom.style.color = "red"
    // 修改class
    dom.classList.add('box')
}

4.2、在iframe的header中添加CSS样式

如果一个元素是动态添加的就获取不到该DOM所以可以通过添加CSS样式的方法。

let header = document.getElementById('frame').contentWindow.document.getElementById('header')
const CSS_STR = `
    .box {
        color: red
    }
`
const style = document.createElement('style')
style.innerText = CSS_STR
header.appendChild(style)

5、兼容性

更多文章