前端多系统集成通信
1:目标
集成多个相互独立的系统展示在一个页面上,多个系统可以相互进行数据通信。
2:应用场景
假设页面内嵌两个系统,就是一个父系统集成2个子系统
一般会存在3种信息传递场景:
1: 父——>子
由父系统把消息传递到子系统
2:子1——>父——>子2
🈶子1把消息传递给子2,但是需要父系统作为代理中转
3:子——> 父
由子系统把消息传递给父系统
3:依赖的主要技术——HTML5跨文档消息传递
跨文档消息传送(cross-document messaging),有时候简称为 XDM,指的是在来自不同域的页面间 传递消息。例如,www.wrox.com 域中的页面与位于一个内嵌框架中的 p2p.wrox.com 域中的页面通信。 在 XDM 机制出现之前,要稳妥地实现这种通信需要花很多工夫。XDM 把这种机制规范化,让我们能 既稳妥又简单地实现跨文档通信。——JavaScript高级程序设计
在此不再详细赘述此技术,可通过相关书籍(推荐《JavaScript高级程序设计》)和博客自行学习。
4:设计思路以及细节处理
注意: 前端框架使用vue
4.1: 我们首先解决从父——>子
步骤:
- 首先在父系统中嵌入iframe,注册2个子系统
http://localhost:8082
和http://localhost:8080
是启动的两个前端应用,分别代表app1和app2。父系统是http://localhost:8081
。
<iframe id='app1' src="http://localhost:8082" frameborder="0" width="550" height="800">
</iframe>
<iframe id='app2' src="http://localhost:8080" frameborder="0" width="550" height="800">
</iframe>
2: 父系统触发事件,例如click事件,通过id获取子系统的window,然后发送消息,例如触发的事件名为:postMessage
//封装方法
postMessage () {
let app1Window = window.document.getElementById('app1').contentWindow
let app2Window = window.document.getElementById('app2').contentWindow
let postMstData = {
//{'data':''}}需要传递的数据, ip字符串:目标targetd的ip地址(一定要拼写正确)
'app1Window': [app1Window, {'data': '我是APP1'}, 'http://localhost:8082'],
'app2Window': [app2Window, {'data': '我是APP2'}, 'http://localhost:8080']
}
this.postMstHandler(postMstData)
},
// 父系统进行广播
postMstHandler (obj) {
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
if (obj[key][0].postMessage) {
obj[key][0].postMessage(obj[key][1], obj[key][2])
}
}
}
}
3: 子系统接收数据
以app1为例:
在vue中,可以写在mounted钩子函数中;
在原生js中,可以写在window.onload = function () {}
中,页面渲染的时候进行接收消息事件注册
mounted () {
let _this = this
// 注册window的message事件来监听和接收消息
window.addEventListener('message', function (event) {
// event.origin: 表示来源地址,此处必须写父系统的域名
if (event.origin === 'http://localhost:8081') {
_this.postMst = event.data['data']
// 处理数据逻辑
......
}
})
}
页面展示效果:
4.2: 解决从子——>父
以app1为例
app1系统触发事件,例如click事件,直接获取parent对象(父系统window),通过parent发送消息,例如触发的事件名为:postMessage
postMessage () {
if (parent.postMessage) {
// 此处域名写父系统的
parent.postMessage({'data': '我是从app1发往父系统的字符串'}, 'http://localhost:8081')
}
},
然后需要在父系统里注册message事件,来监测从app1传来的消息
mounted () {
var This = this
window.addEventListener('message', function (event) {
if (event.origin === 'http://localhost:8082') {
//event.data为 {'app1': data, location: 'http://localhost:8080'}
let keys = Object.getOwnPropertyNames(event.data)
console.log(keys)
if (keys.length === 2) {
// 子 -> 父 -> 子
let app = keys[0]
let appWindow = window.document.getElementById(app).contentWindow
let postMstData = {
'appWindow': [appWindow, {'data': event.data[app]}, event.data.location]
}
console.log(postMstData)
This.postMstHandler(postMstData)
} else {
// 子 -> 父
This.postMst = event.data['data']
}
}
})
},
页面展示效果:
4.3: 解决从子——>父——>子
解决办法是需要通过父系统作为代理来进行传递数据,我们从app1 将数据通过父系统传递给app2
首先在app2触发消息传递
postMessage () {
// 此数据项,表明APP1 要向哪一个APP发送消息,以父模块作为代理
let data = '我是从app1发往app2的字符串'
if (parent.postMessage) {
parent.postMessage({'app2': data, location: 'http://localhost:8080'}, 'http://localhost:8081')
},
父系统接收数据,做转发的准备
mounted () {
var This = this
window.addEventListener('message', function (event) {
if (event.origin === 'http://localhost:8082') {
// {'app1': data, location: 'http://localhost:8080'}
let keys = Object.getOwnPropertyNames(event.data)
console.log(keys)
if (keys.length === 2) {
// 子 -> 父 -> 子
let app = keys[0]
let appWindow = window.document.getElementById(app).contentWindow
let postMstData = {
'appWindow': [appWindow, {'data': event.data[app]}, event.data.location]
}
console.log(postMstData)
This.postMstHandler(postMstData)
} else {
// 子 -> 父
This.postMst = event.data['data']
}
}
})
},
app2注册message事件,来接收从app来的消息
mounted () {
let _this = this
window.addEventListener('message', function (evnet) {
console.log(event.origin)
if(event.origin === 'http://localhost:8081') {
_this.postMst= event.data['data']
}
})
},
页面展示效果如下: