优秀的编程知识分享平台

网站首页 > 技术文章 正文

关于Vue无法对iframe进行缓存的解决方案记录

nanyue 2025-01-09 15:10:59 技术文章 3 ℃

项目背景

项目采用的若依框架,但系统中会嵌入大屏、报表页面,是使用iframe来实现的。

若依框架的菜单管理中提供了缓存功能,是使用keep-alive实现的,但对于iframe页面并不生效,由于大屏页面加载本来就较慢,当用户切换tab页时,iframe要重载并且无法记录之前的操作内容,这非常影响用户体验。

keep-alive

keep-alive的缓存原理,其实是缓存的vnode,它的本质就是js对象,是一串数据,而对于iframe,缓存到的只有一个url,无法知悉其内部的结构与数据,因此无法进行缓存。

解决思路

绕开路由管理,在最外层文件(App.vue)中实现iframe动态加载,并搭配v-show控制iframe显示/隐藏。相当于通过定位,将一个独立的iframe页面覆盖在原有页面的最上方,

具体实现

  • 配置菜单时,将含有iframe的页面多加一个配置参数 isIframe:true,用于前端区分
  • store中存储:缓存的iframe信息{path,url}数组,菜单栏状态(根据菜单展开折叠状态来确定iframe的宽度)
const useIframesStore = defineStore(
    'iframes',
    {
      state: () => ({
        cacheIframes: [],
      }),
      actions: {
        addIframe(iframe) {
            this.cacheIframes.push(iframe)
        },
        delIframe(path) {
          this.cacheIframes = this.cacheIframes.filter(item => item.path !== path)
        },
        clearCache() {
          this.cacheIframes = []
        }
      }
    }
)
  • App.vue:v-for加载store中的已缓存的iframe信息,v-show的控制条件为比对当前路由的path于iframe的path是否一致。
  • 样式问题:因为iframe组件不在router-view内,那么它和菜单是毫无关联的,需要独立设置其样式;为了正常显示效果,采用绝对定位的方式将它悬浮在正常页面的位置;width需要根据store中存储的菜单状态去显示对应的宽度
<template>
  <router-view/>

  <!-- iframe页 -->
  <div v-if="route.path!=='/login'" :class="`iframes w-menu${sidebar.opened?'1':'0'}`" :style="`height:${iframeHeight};z-index:${route.query.isIframe?'999':'-999'}`">
    <Iframe v-for="i in cacheIframes" :key="i.path" :src="i.url" v-show="route.path === i.path" title="iframe"></Iframe>
  </div>
</template>
...
<style lang="scss" scoped>
.w-menu1{
  width: calc(100% - #{$base-sidebar-width});
}
.w-menu0{
  width: calc(100% - 54px);
}
</style>
  • 路由守卫beforeEach:跳转路由时,根据路由参数isIframe为true时,表示即将跳转到iframe页面,调用store的方法往iframe数组中添加iframe信息
router.beforeEach((to, from, next) => {
  const iframesStore = useIframesStore();
  // 跳转iframe页
  if(to.query.isIframe){
    if(iframesStore.cacheIframes.filter(item => item.path === to.path).length==0){
      // 添加缓存
      iframesStore.addIframe({
        path: to.path,
        url: to.query.iframeUrl
      })
    }
  }
  next();
})
  • 关闭tab签时:删除store中对应的iframe项
function closeSelectedTag(view) {
  if(view.query.isIframe){
    iframesStore.delIframe(view.path);
  }
  ...
}
  • 用户退出登录后,清空缓存
...
logOut() {
    return new Promise((resolve, reject) => {
      logout(this.token).then(() => {
        this.token = ''
        this.roles = []
        this.permissions = []
        removeToken()
        // 清空缓存
        useIframesStore().clearCache()
        resolve()
      }).catch(error => {
        reject(error)
      })
    })
}
  • 在若依菜单管理中将iframe页面,使用参数isIframe控制,而对应的组件页面设置为一个空白页,以避免同时加载两个iframe占用资源。
最近发表
标签列表