vue 应用缓存 iframe 页面
在我们平常开发项目中,可能会存在一些其它项目嵌入的问题,如果项目嵌入过多,可以考虑使用微前端的方式。使用微前端在项目嵌入不多的情况,不建议用,一是初期需要花费不少时间,二是需要上手学习,都需要不少的成本时间。其实早在微前端之前,应用嵌入大多数都是使用的 iframe 嵌入方式。这个使用起来比较方便,但也有一些问题,比如应用之前的数据传递问题,还有就是应用内的弹框问题。在数据传递这一方面可以使用 postMessage 来实现数据的传递。使用 iframe 嵌入应用,页面缓存显得很关键,在没有缓存的情况下,每次切换到对应的 iframe 菜单,重新加载应用,这样是很浪费时间,影响用户体验。
实现思路
1、在 iframe 页面路由注册时,通过一定的规则区分 iframe 路由页面和非 iframe 路由页面。
2、手动获取所有的 iframe 路由页面数据,并且手动注册所有 iframe 路由页面。
3、页面初始化和路由切换时设置对应的 iframe 路由页面应用为已打开状态。
4、获取所有的已打开 iframe 路由页面数据,使用 v-for 循环,并且使用 v-show 控制页面显示隐藏。v-show 就是实现缓存的关键。
区分路由页面
通过在注册路由时在 meta 数据里面添加一个标识,用以区分是否 iframe 路由页面。 代码:通过 isIframe 标识控制是否是 iframe 页面
export default {
path: '/parent',
component: Layout,
redirect: '/parent/index',
name: 'Parent',
meta: {
title: '父菜单',
icon: 'example'
},
children: [{
path: 'child',
name: 'Child',
iframeComponent: () => import('@/views/parent/child'),
meta: {
title: '子菜单',
isIframe: true,
icon: 'example'
}
},
]
获取所有 iframe 路由页面并且手动注册
// 获取所有iframe路由页面
methods:{
getComponentsArr() {
const {routes = [] } = this.$router.options
const data = routes.reduce((pre, item) => {
if (!Array.isArray(item.children)) return pre
item.children.forEach((cItem) => {
const { meta = {}, name, path, iframeComponent } = cItem
if (meta.isIframe) {
const obj = {
name,
path: item.path + '/' + path,
hasOpen: false, // 是否打开过,默认false
component: iframeComponent
}
pre.push(obj)
}
})
return pre
}, [])
return data
},
},
created:{
const componentsArr = this.getComponentsArr()
// 遍历注册iframe路由页面
componentsArr.forEach((item) => {
Vue.component(item.name, item.component)
})
}
设置 iframe 路由在打开后标记为打开状态,方便缓存
methods:{
// 通过设置iframe路由页面的hasOpen属性获取页面是否打开过
isOpenIframePage() {
const target = this.componentsArr.find(item => {
return item.path === this.$route.path
})
if (target && !target.hasOpen) {
target.hasOpen = true
}
},
},
computed:{
// 实现懒加载,只渲染已经打开过(hasOpen:true)的iframe页
hasOpenComponentsArr() {
return this.componentsArr.filter(item => {
return item.hasOpen
})
}
}
使用 iframe 路由组件
// v-show 控制组件的显示隐藏 实现类似于缓存的机智
<component v-for="item in hasOpenComponentsArr"
:key="item.name"
v-show="item.path===$route.path"
:is="item.name"></component>
完整代码
<template>
<div class="app-mainWrap">
<section class="app-main">
<template >
<keep-alive v-if="cachedViews">
<router-view/>
</keep-alive>
<router-view v-else></router-view>
</template>
<!--iframe页(keep-alive 不能缓存iframe 单独通过v-show 控制)-->
<component v-for="item in hasOpenComponentsArr"
:key="item.name"
v-show="item.path===$route.path"
:is="item.name"></component>
</section>
</div>
</template>
<script>
export default {
name: 'AppMain',
data() {
return {
componentsArr: [],
}
},
computed: {
// 实现懒加载,只渲染已经打开过(hasOpen:true)的iframe页
hasOpenComponentsArr() {
return this.componentsArr.filter(item => {
return item.hasOpen
})
}
},
watch: {
// 判断当前路由是否iframe页
$route: {
handler(to, from) {
this.isOpenIframePage()
}
}
},
created() {
// 设置iframe页的数组对象
const componentsArr = this.getComponentsArr()
componentsArr.forEach((item) => {
Vue.component(item.name, item.component)
})
this.componentsArr = componentsArr
// 判断当前路由是否iframe页
this.isOpenIframePage()
},
methods: {
// 根据当前路由设置hasOpen
isOpenIframePage() {
const target = this.componentsArr.find(item => {
return item.path === this.$route.path
})
// console.log(target, 12313)
if (target && !target.hasOpen) {
target.hasOpen = true
}
},
// 遍历路由的所有页面,把含有iframeComponent标识的收集起来
getComponentsArr() {
const router = this.$router
const routes = router.options.routes
const data = routes.reduce((pre, item) => {
if (!Array.isArray(item.children)) return pre
item.children.forEach((cItem) => {
const { meta = {}, name, path, iframeComponent } = cItem
if (meta.isIframe) {
const obj = {
name,
path: item.path + '/' + path,
hasOpen: false, // 是否打开过,默认false
component: iframeComponent
}
pre.push(obj)
}
})
return pre
}, [])
return data
},
}
}
</script>
结语
主要是工作中问题的一些个人记录,文章比较简单,主要是为了加深个人记忆及供掘友们参考。
自己写的难免有一些不足之处,欢迎掘友指正。
参考文章:vue缓存iframe的解决方法