作者:管理员 历史版本:1 更新时间:2024-11-20 15:41
需求
页面跨域消息传递
A页面中用iframe加载了B页面, 这两个页面不同源,需要双向通讯。
页面跨浏览器窗体消息传递
同一个浏览器同时打开A页面 和 B页面, 这两个页面不同源,也需要双向通讯。
原理
postMessage
postMessage
可以实现不同源的页面在iframe中通讯, 但是不能跨浏览器窗体。
localStorage
当 localStorage
存储的值发生变化时会在当前窗体window触发 storage 事件。 多个同源的页面是共享localStorage
,因此浏览器同时打开多个同源页面,只要localStorage
改变了,都会收到 storage 事件。
Bridge 桥
postMessage
可不同源通讯,但是不能跨窗体,localStorage
可以跨窗体,但是只限同源页面。 不能满足跨窗体不同源的通讯需求。
为了满足需求,整合postMessage
和localStorage
的特性,实现既能跨窗体也可以不同源的消息通讯方案,我们称这个方案叫 Bridge 桥
就是为两个窗体或页面之间搭一座桥,把消息通过桥传递给对方。
实现
组成
例如:有2个相互独立应用 应用A 和 应用B 他们之间不同源的。
每个应用都自己的 页面
、Bridge
和 localStorage
消息传递过程
同一个浏览器同时打开 应用A的页面
和应用B的页面
,消息通信过程:
页面A
需要发信息给页面B
,首先要知道应用B
的桥地址(桥类库的页面url),才能搭桥(在页面A
创建一个iframe加载应用B
的桥页面)。 搭桥好后,通过postMessage
把信息传递给应用B
的桥
。应用B
的桥
收到消息后,把消息加上时间戳,确保了消息数据的唯一性,并写入到自己的localStorage
。应用B
的页面侦听了storage
事件,当localStorage
发生了变化,即会收到变化后的内容,即收到了页面A
发送过来的数据。- 同理,
页面B
发送信息给页面A
,也需要搭桥,把消息发送给应用A
的桥
应用A
的桥
收到消息后,并写入到自己的localStorage
。应用A
的页面侦听了storage
事件,当localStorage
发生了变化, 即收到了页面B
发送过来的数据。
应用
页面通讯
两个互相独立的页面通讯,可以是iframe嵌套或浏览器新窗口
<template>
<div>
<el-button type="primary" @click="openDialog">弹窗打开</el-button>
<el-button type="primary" @click="openWindow">跨窗口打开</el-button>
<el-button @click="sendMessage">发送消息</el-button>
:
<h3> 收到回复:</h3>
<p v-for="(item, index) in replyList" :key="index">{{item}}</p>
<el-dialog :visible.sync="visible"
:footer="false"
target="body"
draggable
maximizable
title="弹窗"
width="500px"
height="400px"
src="pages/dialog.html">
</el-dialog>
</div>
</template>
<script>
import {fire, on} from '@/utils/bridge'
export default {
data() {
return {
visible: false,
replyList: []
}
},
methods: {
openDialog() {
this.visible = true
},
openWindow() {
window.open('pages/dialog.html')
},
sendMessage() {
const data = {content: `消息内容:${new Date().getTime()}`}
fire({
bridge: '/ibps/bridge/index.html',
channel: 'SendDialogMessageChannel',
data: data
})
this.$message.success('发送成功')
},
messageHandler(data) {
this.replyList.push(data)
}
},
created() {
this.messager = on('ReplyMessageChannel', this.messageHandler)
},
beforeDestroy() {
this.messager && this.messager.destroy()
}
}
</script>
界面服务
界面服务是指,A页面提供了某种服务能力,B页面需要使用A页面的服务得到某些结果,B页面可以对A页面发起服务调用。类似API的调用。
template>
<div>
<el-button @click="callService">调用服务</el-button>
<h3>结果:</h3>
<p v-for="(item, index) in results" :key="index">{{item}}</p>
</div>
</template>
<script>
import {service} from '@/utils/bridge'
export default {
data() {
return {
results: []
}
},
methods: {
callService() {
const opener = window.open('viwes/provider.html')
opener.onload = () => {
service({
name: 'ServiceName',
bridge: '/ibps/bridge/index.html',
origin:'/ibps/bridge/index.html',
data: {
id: new Date().getTime()
},
callback: (data)=>{
this.results.push(data)
}
})
}
}
}
}
</script>