postMessage真香

cAuth發表於2020-01-07

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真香

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、相容性

postMessage真香

更多文章

相關文章