公司项目要求把之前react项目嵌入到vue3.0项目中,想了好久iframe是一种解决方案
需求:
在vue3.0项目中菜单点击时,打开react项目中的一个页面,react页面内部跳转的时候通知vue3.0,打开一个tab(互相通信)
需要注意:
keepalive不缓存iframe 需要找另一个解决方案,把iframe写在route之外
app.vue
<el-main id="app">
<router-view
ref="pageRef"
@hook:mounted="onMounted"
v-slot="{ Component }"
>
<keep-alive :include="keepAliveList" :max="10">
<component :is="Component" v-if="!route.meta.href"/>
</keep-alive>
</router-view>
</el-main>
<div
class="class-for-iframe"
v-for ="item in showIframeTabArr"
:key= "item.name"
v-show="item.href.indexOf(route.meta.href)>-1 && item.name === route.name"
>
<Iframe :key="item.name" :href="item.href" :id="item.name"></Iframe>
</div>
</el-container>
在vue项目中全局监听子组件传过来的消息(APP.VUE文件)
setup () {
window.addEventListener('message', receiveMessage, false);
function receiveMessage (event:any) {
console.log("父組件接收的消息",event);
const showIframeTabArr: ComputedRef<any[]> = computed(() => {
return store.state.tabs.showIframeTabArr;
})
const tabs: ComputedRef<any[]> = computed(() => {
return store.state.tabs.tabs;
})
// const { activeTab, tabList, dispatch } = this.props;
if (event !== undefined && event.data) {
const { domainName,isCloseTab } = event.data;
// iframe里面 跳转新的页面,通知父组件打开,带上参数
if (domainName) {
const obj = {
...event.data,
};
obj.key = event.data.route.split('?')[0].split('/')[
event.data.route.split('?')[0].split('/').length - 1
];
const path = obj.route;
const params = path.split('?');
const iframeUrl = "https://prituat.phfund.com.cn";
const href = params.length>1?`${iframeUrl}${path}&iframe=1` : `${iframeUrl}${path}?iframe=1`;
const selectMenuItem =
findMenuItem(menuList.value, path)
||
{title:obj.title,path:`/${APP_NAME}${path.split('?')[0]}`,name:obj.key,href};
const tabItem = { label: selectMenuItem.title, path: selectMenuItem.path,name:obj.key,href };
if( tabItem.name === 'offsiteSystem' ||
tabItem.name === 'dashboard' ||
tabItem.name === 'newCalendar' ||
tabItem.name === 'etfwebscoket'){
GetChildIframe({...tabItem, orgin: { Interval: '2' }})
}else{
const res = tabs.value.filter((t:any) =>
t.name === 'offsiteSystem'
);
res.map((a:any)=>{
GetChildIframe({...a, orgin: { Interval: '1' }})
})
}
// 按react项目里面的 用component 组件名判断是否重复
if (tabs.value.some(tab => tab.name === selectMenuItem.name)) {
store.dispatch('updateTab', {
index: tabs.value.findIndex(tab => tab.name === selectMenuItem.name),
tab: tabItem
})
store.dispatch('updateShowIframeTabArr', {
index: showIframeTabArr.value.findIndex(tab => tab.name === selectMenuItem.name),
tab: tabItem
})
// console.log("需要更新",tabs);
router.push({path:`/${APP_NAME}${path.split('?')[0]}`,query:{href}});
} else {
// console.log("这是新增");
store.dispatch('createShowIframeTabArr', tabItem)
store.dispatch('createTab', tabItem)
router.push({path:`/${APP_NAME}${path.split('?')[0]}`,query:{href}});
}
}
if (isCloseTab) {
const cur = tabs.value.findIndex(item=> item.path.indexOf(route.path)>-1);
const curiframe = showIframeTabArr.value.findIndex(item=> item.path.indexOf(route.path)>-1);
close(route.path,cur,false);
close(route.path,curiframe,true);
}
}
}
}
iframe.vue
<template>
<iframe
:src="href"
width="100%" height="100%" frameborder="0" scrolling="auto">
</iframe>
</template>
vue中菜单点击跳转页面,打开tab栏直接按照官方文档来即可
需要注意的是:
要两个数组(store)用来存放,一个存“tabs”放vue项目中自己的tab
另一个“showIframeTabArr”用来存放iframe相关的tab
因为iframe中可能存在定时器,跳转到别的页面时需要关闭定时器,只需要调用GetChildIframe给子组件发消息,写在了通用工具里,当然react项目中需要接收到该参数并停止定时器
export function GetChildIframe (data:any) {
const { orgin, path } = data;
const Id = path.split('?')[0].split('/')[
path.split('?')[0].split('/').length - 1
];
// const { currentUser } = window.g_app._store.getState().user;, currentUser
const parent = { ...orgin, noAjaxUser: true };
setTimeout(() => {
const iframes: any = document.getElementById(`${Id}`);
// console.log(iframes);
if(iframes)
iframes.contentWindow.postMessage(parent, '*');
}, 1000);
}