使用postMessage進(jìn)行跨域通信

跨域通信是前端開(kāi)發(fā)中經(jīng)常會(huì)遇到的情景,跨域通信有多種多樣的方式,今天就詳細(xì)說(shuō)一下使用 postMessage 這樣方式進(jìn)行跨頁(yè)面腳本的數(shù)據(jù)通信。

一、認(rèn)識(shí) window.postMessage

根據(jù) MDN 的官方文檔解釋:window.postMessage() 方法可以安全地實(shí)現(xiàn)跨源通信。通常,對(duì)于兩個(gè)不同頁(yè)面的腳本,只有當(dāng)執(zhí)行它們的頁(yè)面位于具有相同的協(xié)議(通常為 https),端口號(hào)(443 為 https 的默認(rèn)值),以及主機(jī) (兩個(gè)頁(yè)面的模數(shù) Document.domain 設(shè)置為相同的值) 時(shí),這兩個(gè)腳本才能相互通信。

二、使用語(yǔ)法

1、向目標(biāo)窗口發(fā)送消息

  otherWindow.postMessage(message,targetOrigin)
  • otherWindow

    其他窗口的一個(gè)引用,比如 iframe 的 contentWindow 屬性、執(zhí)行 window.open 返回的窗口對(duì)象、或者是命名過(guò)或數(shù)值索引的 window.frames。

  • message

    將要發(fā)送到其他窗口的數(shù)據(jù)。

  • targetOrigin

    指定哪些窗口能接收到消息,其值可以是字符串"*"(表示無(wú)限制)或者一個(gè) URI,在發(fā)送消息的時(shí)候,如果目標(biāo)窗口的協(xié)議、主機(jī)地址或端口這三者的任意一項(xiàng)不匹配 targetOrigin 提供的值,那么消息就不會(huì)被發(fā)送;只有三者完全匹配,消息才會(huì)被發(fā)送。這個(gè)機(jī)制用來(lái)控制消息可以發(fā)送到哪些窗口.

2、目標(biāo)窗口接收消息

  window.addEventListener("message", receiveMessage, false);
  function receiveMessage(event){
    // event.origin 表示消息的來(lái)源地址
    // event.data 表示接收到的數(shù)據(jù)
    if (event.origin !== 'http: //www.xxxx.com') {
      return
    } else {
      console.log(event.data)
    }
  }

三、具體案例

父窗口主動(dòng)給子窗口發(fā)消息

本案例是在 Vue2.0+iView3.0 的環(huán)境下進(jìn)行,在一個(gè)彈框內(nèi)嵌套一個(gè) iframe 頁(yè)面。

  • 模板

    <button @click="show">顯示</button>
    <Modal
      v-model="isModalShow"
      width="80%"
      class-name="select_rules_modal"
      footer-hide
      @on-visible-change="visibleModalChange"
    >
      <div class="rules_header">iFrame頁(yè)面</div>
      <div class="rules_body">
        <iframe
          class="rules_iframe"
          src="http://www.target.com:9900/list"
          id="iframe"
          ref="iframe"
          v-if="isModalShow"
        ></iframe>
      </div>
    </Modal>
    
  • 邏輯

    data() {
      return {
        isModalShow: false
      }
    },
    methods: {
      show(){
        this.isModalShow = true
        let iframe = document.getElementById('iframe')
        let data = 'hello'
        if (iframe.attachEvent){ // 兼容IE寫(xiě)法
            // 必須在ifame頁(yè)面加載完畢后才能發(fā)送消息,否則對(duì)方接收不到
            iframe.attachEvent("onload", function(){
              // 發(fā)消息
              iframe.contentWindow.postMessage(data, 'http://target.com:9900')
            })
          } else {
            iframe.onload = function(){
              // 發(fā)消息
              iframe.contentWindow.postMessage(data, 'http://target.com:9900')
          }
        }
        // 收消息(在目標(biāo)頁(yè)面進(jìn)行相關(guān)操作,接收發(fā)送過(guò)來(lái)需要的數(shù)據(jù))
        window.addEventListener('message',this.receiveMessage,false)
      },
      // 接收消息回調(diào)
      receiveMessage(event){
        // 判斷是否是目標(biāo)地址發(fā)過(guò)來(lái)的消息,否則不接收
         if (event.origin !== 'http://target.com:9900') {
          return
        } else {
          // event.data 為接收到的數(shù)據(jù)
          console.log(event.data)
          // 處理數(shù)據(jù).....
          this.isModalShow = false
        }
      },
      // 彈框關(guān)閉時(shí)移除消息監(jiān)聽(tīng)
      visibleModalChange(){
        window.removeEventListener('message',this.receiveMessage,false)
      }
    }
    

    子窗口主動(dòng)給父窗口發(fā)送消息

    思路: 子窗口無(wú)法直接給父窗口發(fā)消息,因?yàn)榇嬖诳缬騿?wèn)題,如果要實(shí)現(xiàn)子窗口給父窗口發(fā)送消息,需要父窗口先給子窗口發(fā)送一個(gè)消息,子窗口記住事件源,然后才可以實(shí)現(xiàn)發(fā)送消息給父窗口。
    1、父窗口在 iframe 加載完后發(fā)送消息,并監(jiān)聽(tīng)子窗口的消息

    let iframe = document.getElementById("iframe");
    if (iframe.attachEvent) {
      // 兼容IE寫(xiě)法
      iframe.attachEvent("onload", function () {
        // 發(fā)消息
        iframe.contentWindow.postMessage(data, "https://yyy.com");
      });
    } else {
      iframe.onload = function () {
        // 發(fā)消息
        iframe.contentWindow.postMessage(data, "https://yyy.com");
      };
    }
    // 收消息
    window.addEventListener("message", this.receiveChildMessage, false);
    .......
    receiveChildMessage(event) {
      //.......
    }
    

    2、子窗口在頁(yè)面加載完畢后監(jiān)聽(tīng)消息,收到消息后保存事件源

    window.addEventListener("message", this.receiveParentMsg, false);
    .....
    receiveParentMsg(event) {
        if (event.origin !== 'http://xxx.com'){
            return
        }
        // 存儲(chǔ)事件源,以便于保存時(shí)發(fā)消息
        this.parentEvent = event
    },
    

    3、事件觸發(fā)時(shí)給父窗口發(fā)送消息

    this.parentEvent.source.postMessage("發(fā)送消息", this.parentEvent.origin);
    

    4、頁(yè)面銷毀時(shí)移除消息監(jiān)聽(tīng)

    window.removeEventListener("message", this.receiveParentMsg, false);
    

    四、注意事項(xiàng)

    1、需要通過(guò)onload的事件監(jiān)聽(tīng)iframe頁(yè)面加載完畢后在發(fā)生消息,發(fā)送接收不到。

    2、一定要在頁(yè)面關(guān)閉的時(shí)候移除監(jiān)聽(tīng)事件,否則會(huì)導(dǎo)致在每打開(kāi)一次iframe頁(yè)面,接收的消息會(huì)疊加一次的問(wèn)題。

    3、如果是父窗口 iframe 加載完后發(fā)消息,子窗口頁(yè)面加載后監(jiān)聽(tīng)消息,一定要注意子窗口監(jiān)聽(tīng)消息要在父窗口發(fā)消息以前,否則可能收不到消息,放在哪個(gè)生命周期中根據(jù)自己情況處理。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 1. 什么是跨域? 跨域一詞從字面意思看,就是跨域名嘛,但實(shí)際上跨域的范圍絕對(duì)不止那么狹隘。具體概念如下:只要協(xié)議...
    w_zhuan閱讀 622評(píng)論 0 0
  • 概述 JavaScript出于安全方面的考慮,不允許跨域調(diào)用其他頁(yè)面的對(duì)象。但在安全限制的同時(shí)也給注入iframe...
    npmstart閱讀 2,316評(píng)論 0 5
  • 原文來(lái)自:http://www.ruanyifeng.com/blog/2016/04/same-origin-p...
    神秘者007閱讀 1,146評(píng)論 0 1
  • 什么是跨域 跨域,是指瀏覽器不能執(zhí)行其他網(wǎng)站的腳本。它是由瀏覽器的同源策略造成的,是瀏覽器對(duì)JavaScript實(shí)...
    HeroXin閱讀 963評(píng)論 0 4
  • 一、概述 1.1 含義 1995年,同源政策由 Netscape 公司引入瀏覽器。目前,所有瀏覽器都實(shí)行這個(gè)政策。...
    會(huì)飛的賊er閱讀 497評(píng)論 0 2

友情鏈接更多精彩內(nèi)容