需求
统一规则、消息处理策略来维护消息通信,参考发布订阅模式实现on监听和emit触发,便于消息事件的跟踪及维护。
封装postMessage.js
class RPCMessageEvent {
constructor (options) {
this._currentTerminal = options.currentTerminal; // 当前终端
this._targetTerminal = options.targetTerminal; // 远程终端
this._targetOrigin = options.targetOrigin || '*'; // 域
this._handlers = {}; // 响应函数
const receiveMessage = pEvent => {
const { event, args } = pEvent.data;
const handlers = this._handlers[event] || [];
handlers.length && handlers.forEach(handler => {
handler(...(args || []));
});
};
this._currentTerminal.addEventListener('message', receiveMessage, false);
}
on (event, handler) {
const handle = this._handlers[event];
this._handlers[event] = handle ? this._handlers[event].push(handler) : [handler];
}
emit (event, ...args) {
this._targetTerminal.postMessage({ event, args }, this._targetOrigin);
}
off (event, handler) {
if (!this._handlers[event]) { return; }
if (!handler) {
this._handlers[event] = [];
return;
}
this._handlers[event] = this._handlers[event].filter(fn => handler !== fn);
}
}
/**
* @param {*} config
* 父域向子域iframe通知消息: rpcMessage(iframeDom).emit('eventName', data); 接收子域消息: rpcMessage(iframeDom).on('eventName', data=>{})
* 子域iframe向父域通知消息: rpcMessage().emit('eventName', data); 接收父域消息: rpcMessage().on('eventName', data=>{})
* 注意:页面销毁时需调用 off() 方法注销掉对应 rpcMessage
*/
const rpcMessage = (config)=>{
let defaultConfig = {
currentTerminal: window,
targetTerminal: window.top,
targetOrigin: '*'
}
if(config){
if(Object.prototype.toString.call(config).toLowerCase() == "[object object]"){
defaultConfig = Object.assign(defaultConfig, config)
}else{
defaultConfig.targetTerminal = config.contentWindow;
}
}
return new RPCMessageEvent(defaultConfig)
}
export default rpcMessage
父域页面的接收和发送
<template>
<div>
<iframe ref="iframeBox" :height="iframeHeight" style="width: 100%;" src="http://localhost:8080/vue-plugin-hiprint/" frameborder="0"></iframe>
</div>
</template>
<script lang='ts' setup>
import { ref, onMounted, onUnmounted } from 'vue';
import rpcMessage from '@/utils/postMessage.js'
let iframeBox = ref<any>(null);
let iframeHeight = ref(600)
let rpcMessageObj = null;
const sendData = (data)=>{
setTimeout(() => {
rpcMessageObj.emit('sendSelectDataSource', { aaa: '向子域发送消息' })
}, 3000);
}
onMounted(()=>{
iframeHeight.value = document.documentElement.clientHeight - 170; // 设置iframe高度
iframeBox.value.onload = function (e) {
rpcMessageObj = rpcMessage(iframeBox.value);
rpcMessageObj.on('selectDataSource', data=>{
console.log('接收子域消息', data)
sendData(data)
})
};
})
onUnmounted(()=>{
rpcMessageObj.off('selectDataSource')
})
</script>
子域页面的接收和发送
<template>
<div>
<button @click="demo">触发按钮</button>
</div>
</template>
<script lang='ts' setup>
import { ref, onMounted, onUnmounted } from 'vue';
import rpcMessage from '@/utils/postMessage.js'
let rpcMessageObj = rpcMessage()
const demo = ()=>{
rpcMessageObj.emit('selectDataSource', { bbb: '向父域发送消息' })
}
onMounted(()=>{
rpcMessageObj.on('sendSelectDataSource', data=>{
console.log('接收父域消息', data)
})
})
onUnmounted(()=>{
rpcMessageObj.off('sendSelectDataSource')
})
</script>