vue---iframe的postMessage跨域通信

前端爬坑日记之vue内嵌iframe并跨域通信

下面将3天的爬坑最终以问答的方式总结如下:

1、Vue组件中如何引入iframe?

2、vue如何获取iframe对象以及iframe内的window对象?

3、vue如何向iframe内传送信息?

4、iframe内如何向外部vue发送信息?

1、Vue组件中如何引入iframe?

<template>
  <div class="act-form">
    <iframe :src="src"></iframe>
  </div>
</template>

<script>

export default {
  data () {
    return {
      src: '你的src'
    }
  }
}
</script>
如上,直接通过添加iframe标签,src属性绑定data中的src,第一步引入就完成了

2、vue如何获取iframe对象以及iframe内的window对象?

在vue中,dom操作比不上jquery的$('#id')来的方便,但是也有办法,就是通过ref
<template>
  <div class="act-form">
    <iframe :src="src" ref="iframe"></iframe>
  </div>
</template>

<script>

export default {
  data () {
    return {
      src: '你的src'
    }
  },
  mounted () {
    // 这里就拿到了iframe的对象
    console.log(this.$refs.iframe)
  }
}
</script>

然后就是获取iframe的window对象,因为只有拿到这个对象才能向iframe中传东西

<template>
  <div class="act-form">
    <iframe :src="src" ref="iframe"></iframe>
  </div>
</template>

<script>

export default {
  data () {
    return {
      src: '你的src'
    }
  },
  mounted () {
    // 这里就拿到了iframe的对象
    console.log(this.$refs.iframe)
    // 这里就拿到了iframe的window对象
    console.log(this.$refs.iframe.contentWindow)
  }
}
</script>

3、vue如何向iframe内传送信息?

通过postMessage,具体关于postMessage是什么,自己去google,

我的理解postMessage是有点类似于UDP协议,就像短信,是异步的,你发信息过去,但是没有返回值的,只能内部处理完成以后再通过postMessage向外部发送一个消息,外部监听message

为了让postMessage像TCP,为了体验像同步的和实现多通信互不干扰,特别制定的message结构如下
{
  cmd: '命令',
  params: {
    '键1': '值1',
    '键2': '值2'
  }
}

通过cmd来区别这条message的目的

具体代码如下

<template>
  <div class="act-form">
    <iframe :src="src" ref="iframe"></iframe>
    <div @click="sendMessage">向iframe发送信息</div>
  </div>
</template>

<script>

export default {
  data () {
    return {
      src: '你的src',
      iframeWin: {}
    }
  },
  methods: {
    sendMessage () {
      // 外部vue向iframe内部传数据
      this.iframeWin.postMessage({
        cmd: 'getFormJson',
        params: {}
      }, '*')
    },
  },
  mounted () {
    // 在外部vue的window上添加postMessage的监听,并且绑定处理函数handleMessage
    window.addEventListener('message', this.handleMessage)
    this.iframeWin = this.$refs.iframe.contentWindow
  },
  handleMessage (event) {
    // 根据上面制定的结构来解析iframe内部发回来的数据
    const data = event.data
    switch (data.cmd) {
      case 'returnFormJson':
        // 业务逻辑
        break
      case 'returnHeight':
        // 业务逻辑
        break
    }
  }
}
</script>

4、iframe内如何向外部vue发送信息?

现在通过点击“向iframe发送信息”这个按钮,从外部vue中已经向iframe中发送了一条信息

{
  cmd: 'getFormJson',
  params: {}
}

那么iframe内部如何处理这个信息呢?

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>iframe Window</title>
    <style>
        body {
            background-color: #D53C2F;
            color: white;
        }
    </style>
</head>
<body>

    <h1>Hello there, i'm an iframe</h1>

    <script>
        // 向父vue页面发送信息
        window.parent.postMessage({
            cmd: 'returnHeight',
            params: {
              success: true,
              data: document.body.scrollHeight + 'px'
            }
        }, '*');

        // 接受父页面发来的信息
        window.addEventListener("message", function(event){
          var data = event.data;
          switch (data.cmd) {
            case 'getFormJson':
                // 处理业务逻辑
                break;
            }
        });
    </script>
</body>
</html>

至此内部的收发信息已经解决了,外部的收发也已经解决了,快去解决你的问题吧

在这里先直接给出我项目的源码

<template>
  <div class="act-form">
    <div class="nav">
      <img src="https://cxkccdn.oss-cn-shanghai.aliyuncs.com/lesai_img/icon_back_white.png" @click="back()">
      <div class="title">报名</div>
    </div>
    <div class="iframe-out">
      <iframe :src="src" ref="iframe" @load="iframeLoad"></iframe>
    </div>
    <div v-if="isLoaded" class="send-form"><div class="send" @click="sendMessage()">提交</div></div>
  </div>
</template>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style lang="sass" rel="stylesheet/sass">
  @import "style.scss";
</style>

<script>
import { Toast, Indicator } from 'mint-ui'
import api from '@/utils/api'

export default {
  data () {
    return {
      src: '',
      iframeWin: null,
      isLoaded: false
    }
  },
  created () {
    let matchFamily = this.$store.state.matchFamily
    this.src = process.env.BASE_URL + '/matches/' + matchFamily.match.id + '/act/' + matchFamily.act.id + '/joinweb?token=' + this.$store.state.token
  },
  mounted () {
    window.addEventListener('message', this.handleMessage)
    this.iframeWin = this.$refs.iframe.contentWindow
    // 开启加载动画
    Indicator.open({
      text: '努力加载中...',
      spinnerType: 'triple-bounce'
    })
  },
  methods: {
    back () {
      this.$router.push('/actIntro')
    },
    sendMessage () {
      this.iframeWin.postMessage({
        cmd: 'getFormJson',
        params: {}
      }, '*')
    },
    iframeLoad () {
      // 关闭加载动画
      Indicator.close()
    },
    async handleMessage (event) {
      const data = event.data
      switch (data.cmd) {
        case 'returnFormJson':
          if (data.params.success) {
            // 调用报名方法
            await this.enroll(data.params.data)
          } else {
            console.log('returnFormJson失败')
            console.log(data.params)
          }
          break
        case 'returnHeight':
          if (data.params.success) {
            this.$refs.iframe.height = data.params.data
            this.isLoaded = true
          }
          break
      }
    },
    async enroll (data) {
      let matchFamily = this.$store.state.matchFamily
      let result = await api.enroll(matchFamily.match.id, matchFamily.act.id, data)
      if (result.success) {
        if (result.data.status === 'no_pay') {
          // 更新缓存
          let resultMatch = await api.match(matchFamily.match.id, {})
          if (resultMatch.success) {
            this.$store.commit('SET_CURRENT_MATCH', resultMatch.data)
          }
          Toast({
            message: '报名成功',
            position: 'bottom'
          })
          this.$router.push('/match/' + matchFamily.match.id + '/mdetail')
        } else {
          console.log('需要跳转到支付页面')
        }
      }
    }
  }
}
</script>
<think>嗯,用户问的是如何使用postMessage实现iframe通信,需要示例教程。首先,我得回忆一下相关的知识。根据之前提供的引用内容,postMessage是HTML5的一个方法,允许不同源的窗口之间安全地通信。同源策略会阻止的DOM访问,但postMessage可以绕过这个限制。 首先,我需要明确基本步骤。根据引用[5],父页面获取iframe的contentWindow,然后用postMessage发送消息。而iframe内部则通过message事件监听消息,反之亦然。同时要注意origin验证,防止安全漏洞。 然后,用户可能需要一个具体的例子。引用[1]和[2]提到了使用postMessage和message事件,所以应该分父页面和子页面两部分来写代码示例。比如父页面发送消息给iframeiframe接收后回复,父页面再处理回复。这样形成一个完整的交互流程。 还要注意的安全问题。在message事件处理中,必须检查event.origin是否是可信的来源,避免恶意网站发送消息。这点在引用[3]和[4]中也有提到,所以示例代码中需要包含origin检查的部分。 可能用户还会关心获取iframe的window对象的几种方法,比如通过name、id或者Vue中的ref。引用[5]提到了这三种方式,特别是Vue特有的方法,所以如果用户使用Vue的话,这部分需要特别说明。 另外,数据格式方面,postMessage的第一个参数可以是字符串或对象,但旧版浏览器可能不支持对象,所以建议用JSON.stringify转换。这点需要提醒用户,确保兼容性。 最后,常见问题部分应该覆盖通信失败的可能原因,比如未正确获取iframe的window对象、没有监听message事件,或者origin验证错误。这些都是用户在实际操作中容易遇到的问题,需要一一解答。 总结一下,回答的结构应该是先解释原理,再分步骤给出示例代码,强调安全措施,然后列出常见问题和注意事项,最后生成相关问题。确保每一步都清晰,并且示例代码正确,符合用户的实际应用场景。</think>### 如何通过 `postMessage` 实现 iframe 通信 #### 一、实现原理 `postMessage` 是 HTML5 提供的通信 API,通过以下步骤实现安全通信: 1. **发送消息**:父页面或子页面调用 `postMessage(data, targetOrigin)` 发送数据。 2. **接收消息**:通过监听 `message` 事件处理数据[^2][^3]。 #### 二、具体步骤与代码示例 ##### 1. 父页面发送消息给 iframe ```html <!-- 父页面 parent.html --> <iframe id="childFrame" src="https://child-domain.com/child.html"></iframe> <script> const iframe = document.getElementById('childFrame'); // 等待 iframe 加载完成 iframe.onload = () => { // 通过 contentWindow 获取子窗口对象[^5] const childWindow = iframe.contentWindow; // 发送消息(数据 + 目标名) childWindow.postMessage( { type: 'greeting', data: 'Hello from parent!' }, 'https://child-domain.com' // 限制接收方名 ); }; // 监听子页面回复 window.addEventListener('message', (event) => { // 验证来源名 if (event.origin !== 'https://child-domain.com') return; console.log('Received from child:', event.data); }); </script> ``` ##### 2. 子页面接收并回复 ```html <!-- 子页面 child.html --> <script> // 监听父页面消息 window.addEventListener('message', (event) => { // 验证来源名 if (event.origin !== 'https://parent-domain.com') return; console.log('Received from parent:', event.data); // 向父页面发送回复 event.source.postMessage( { type: 'response', data: 'Message received!' }, event.origin // 直接使用来源名确保安全 ); }); </script> ``` #### 三、关键注意事项 1. **安全验证** - 必须通过 `event.origin` 校验消息来源名,防止恶意攻击 - 使用 `targetOrigin` 参数限制消息发送范围(支持通配符 `*`,但不推荐) 2. **数据格式** - 支持字符串、对象、数组等可序列化数据 - 复杂对象建议用 `JSON.stringify` 序列化 3. **兼容性** - 支持所有现代浏览器(IE8+ 需降级处理) #### 四、常见问题排查 | 问题现象 | 可能原因 | 解决方案 | |---------|---------|---------| | 消息未发送 | iframe 未加载完成 | 将代码放在 `iframe.onload` 回调中执行 | | 接收方无响应 | 未监听 `message` 事件 | 检查事件监听绑定逻辑 | | 错误 | `targetOrigin` 不匹配 | 确保名协议、主机、端口一致 | #### 五、进阶场景 - **多级嵌套 iframe**:通过逐级传递 `window.parent` 实现层级通信[^4] - **Vue/React 框架集成**:使用 `this.$refs.iframe.contentWindow` 获取子窗口对象[^5]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值