1、父发消息给子,接收来自子的消息
父页面相关代码
<div class="iframeWrap">
<iframe ref="udaamIframe" class="udaamIframe" style="width:100%;height:100%" :src="linkUrl" frameborder="0" />
</div>
data() {
return {
linkUrl: this.$route.meta.linkUrl
}
},
// vue挂载阶段
mounted() {
this.iframeTalk()
},
methods: {
iframeTalk() {
// 有这个参数 needSendToken 的才需要进行通信
if (this.linkUrl.includes('needSendToken=true')) {
const iframe = this.$refs.udaamIframe // 这个是vue的语法,可以用下面一行替换
// document.querySelector('.iframeWrap .udaamIframe')
var fatherApp = iframe.contentWindow
// 处理兼容行问题
if (iframe.attachEvent) {
// 当 iframe 加载好了再发消息,不然iframe收不到
iframe.attachEvent('onload', () => {
this.sendToken(fatherApp)
})
} else {
iframe.onload = () => {
this.sendToken(fatherApp)
}
}
window.udemrFirstLoginOut = true // 避免触发多次
// 接收子应用消息
window.addEventListener('message', e => {
// <!-- 对消息来源origin做一下过滤,避免接收到非法域名的消息导致的xss攻击 -->
if (e.data && e.data.origin === 'child' && e.data.isLoginOut === true && window.udemrFirstLoginOut) {
console.log('收到child发来的消息 需要协助其退出登录, e:', e)
window.udemrFirstLoginOut = false
store.dispatch('LogOut')
}
}, false)
}
},
sendToken(udaamApp) {
console.log('父 发消息给外链子应用,消息内容为token')
udaamApp.postMessage(
{
type: 'sendToken',
token: localStorage.getItem('token'),
remark: '阿巴阿巴阿巴'
},
'*'
)
}
}
2、子接收来自父的消息、发消息给父
子页面相关代码
// 接收来自父发来的消息
window.addEventListener('message', function(e) {
if (e.data && e.data.type === 'sendToken') {
console.log('收到来自主应用的消息', e)
localStorage.setItem('token', e.data.token)
}
})
// 子发消息给父
const params = {
origin: 'child',
isLoginOut: true
}
window.parent.postMessage(params, '*')
补充
由于 加载 iframe 是异步的、 iframe收到主应用发来的消息也是异步的、iframe 根据路由加载对应的模块也是异步的、对应模块加载时什么时候发起api请求也是异步;
这么多异步,已经避免的是主应用发消息一定是在iframe 加载后能收到消息后才发的消息,即子应用iframe一定能正常收到主应用的消息;
但是子应用 iframe 收到消息 和 其页面调api请求是不可控的,而我们又需要 iframe收到消息之后再发起请求(需要携带父发来的token),所以需要处理一下:
思路如下: 找到需求发请求的模块,找出其需要最先发的请求,在发请求前先轮询等待接收主应用的消息,直到收到消息后再结束轮询正常发起api请求加载页面;
// 等待接收到主应用发来的消息后 再进行api请求(因为发请求需要携带由主应用传过来的token)
timeout: null
setWaitToken() {
const getToken = () => {
this.timeout && clearTimeout(this.timeout)
this.timeout = setTimeout(() => {
if (localStorage.token) {
this.yourFirstApi() // 该页面需要第一个发起的api请求
} else {
getToken()
}
}, 100)
}
getToken()
},