背景
什么时候我们需要使用Vue3内嵌iFrame呢?答案是当我们多个项目需要共享一个页面的时候,我们需要在当前的Vue3的项目中内嵌一个iFrame的页面。或我们需要在一个网站嵌入另外一个网站。我们现在就有这样的一个使用场景,我们左侧有一个导航栏,这个导航栏可以点击跳转到不同的页面,一部分页面是本地的组件,一部分的页面是iFrame的页面。
问题
在实现的过程中我们遇到了如下的问题:
- 当外侧的参数传入的时候,我们需要对外部的参数进行监听,刷新内部的页面
- 当点击的时候,我们要根据不同的链接,自动的去渲染到正确的页面
- 当我们在
iFrame
的页面进行刷新的时候,我们还要停留在当前的页面
操作
搭建
我们需要在Wrapper
的项目中,定义出一个iFrame
的页面,我们在创建之前,在我们开始操作之前,我们一定是有一个这样的页面,写过Vue3的人都应该清楚router-view
的作用,我们在这里就不再过多的描述。
父页面的项目.vue
<a-layout-content>
<router-view></router-view>
</a-layout-content>
我们的左侧导航栏一般都是和后端的返回的路由绑定的,我们现在可以知道,我们之前的后端返回的数据结构如下:
json
{
"code": 200,
"msg": "操作成功",
"data": [
{
"name": "Monitoring",
"path": "/Monitoring",
"hidden": false,
"redirect": "noRedirect",
"component": "Layout",
"alwaysShow": true,
"meta": {
"title": "父级",
"link": null
},
"children": [
{
"name": "Load",
"path": "load",
"hidden": false,
"component": "monitoring/load/index",
"meta": {
"title": "子组件",
"noCache": false,
"link": null
}
},
{
"name": "IframeChart",
"path": "iframeChart",
"hidden": false,
"component": "https://www.baidu.com/#/iframe",
"meta": {
"title": "子组件-iFrame",
"noCache": false,
"link": null
}
}
]
}
]
}
在接入iFrame
,上面都是我们的准备工作,接下来我们开始接入iFrame
,首先我们先创建一个页面,这个页面,我们只用来显示iFrame
的内容。具体的内容如iframe.vue
下:
js
<template>
<iframe :src="iframeSrc" width="100%" height="100%" frameborder=""no"" border=""0″"/>
</template>
<script setup>
import { useStore } from 'vuex';
const store = useStore();
const iframeSrc = ref('');
const token = localStorage.getItem('token');
onMounted(() => {
iframeSrc.value = `${route.meta.link}?token=${token}&source=a`;
token=${token}&source=saas`;
});
</script>
我们有一个加载组件的公共方法:
js
export const loadView = (view) => {
return () => import(`@/views/${view}.vue`);
};
当然在这之前,我们需要对我们的路由进行处理,具体的处理逻辑如下:
ps:其中loadView('iframe')
加载的就是我们之前写的iframe.vue
js
if (route.component.includes('http')) {
route.meta.link = route.component;
route.meta.isIframe = true;
route.component = loadView('iframe');
}
我们这样操作下来,就完成了当我们在iFrame
的页面进行刷新的时候,页面没有停留在当前页面的问题。接下来我们需要解决的。接下来我们需要解决的就是通信的问题,我们的父组件需要给子项传入一些必要的信息,在我们之前的写法中,token
我们直接放在了url
中进行通信,当时当我们切换用户或是切换项目的时候,我们需要对页面进行刷新。我们一般是在iframe
加载后,对子组件进行消息发送,我们一般使用postMessage
。我们根据这个要求,对iframe.vue
文件进行代码更新,更新后的结果如下:
js
<template>
<iframe :src="iframeSrc" width="100%" ref="iframePageRef" @load="onIframeLoad" height="100%" frameborder=""no"" border=""0″"></iframe>
</template>
<script setup>
import { useStore } from 'vuex';
import { useRoute } from 'vue-router';
const route = useRoute();
const store = useStore();
const iframePageRef = ref(null);
const iframeSrc = ref('');
const token = localStorage.getItem('token');
onMounted(() => {
iframeSrc.value = `${route.meta.link}/#/energyChart?token=${token}&source=saas`;
});
const onIframeLoad = () => {
iframePageRef.value.contentWindow.postMessage(
{
projectId: store.state.projectId,
},
'*',
);
};
</script>
然后我们在子项目中进行接收,接收的内容,我们在App.js
中进行监听
js
window.addEventListener('message', function (event) {
if (event.data && event.data.projectId) {
sessionStorage.setItem('projectId', event.data.projectId);
store.commit('SET_ProjectId', event.data.projectId);
}
});
这样的话,我们在项目刚开始渲染的时候,就可以拿到最新的projectId
了,然后我们在projectId
改变的时候需要通知子项目,这个时候子项目需要监听store
中的projectId
,子项目的的监听方法如下:
js
watch(
() => store.state.projectId,
() => {
queryData(); // 一些查询相关的操作
},
);