前端爬坑日记之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>