当前位置: 首页 > article >正文

keep-alive缓存不了iframe

最近做了个项目,其中有个页面是由 iframe 嵌套了一个另外的页面,在运行的过程中发现 KeepAlive 并不生效,每次切换路由都会触发 iframe 页面的重新渲染,代码如下:

  <router-view v-slot="{ Component }">
    <keep-alive :include="keepAliveList">
      <component :is="Component"></component>
    </keep-alive>
  </router-view>

看起来并没有什么问题,并且其他非 iframe 实现的页面都是可以被缓存的,因此可以推断问题出在 iframe 的实现上。

我们先了解下 KeepAlive

KeepAlive (熟悉的可跳过本节)

被 KeepAlive 包裹的组件不是真的卸载,而是从原来的容器搬运到另外一个隐藏容器中,实现“假卸载”, 当被搬运的容器需要再次挂载时,应该把组件从隐藏容器再搬运到原容器,这个过程对应到组件的生命周期就是 activated 和 deactivated

keepAlive 是需要渲染器支持的,在执行 mountComponent 时,如果发现是 __isKeepAlive 组件,那么会在上下文注入 move 方法。

function mountComponent(vnode, container, anchor) {
  /**... */
  const instance = {
    /** ... */
    state,
    props: shallowReactive(props),
    // KeepAlive 实例独有
    keepAliveCtx: null
  };

  const isKeepAlive = vnode.__isKeepAlive;
  if (isKeepAlive) {
    instance.keepAliveCtx = {
      move(vnode, container, anchor) {
        insert(vnode.component.subTree.el, container, anchor);
      },
      createElement
    };
  }
}

原因

通过上面的了解,我们知道,KeepAlive 缓存的是 vnode 节点,vnode 上面会有对应的真实DOM。组件“销毁”时,会将真实 DOM 移动到“隐藏容器”中,组件重新“渲染”时会从 vnode 上取到真实 DOM,再重新插入到页面中。这样对普通元素是没有影响的,但是 iframe 很特别,当其插入到页面时会重新加载,这是浏览器特性,与 Vue 无关。

解决方案

思路:路由第一次加载时将 iframe 渲染到页面中,路由切换时通过 v-show 改变显/隐。

  1. 在路由注册时,将 component 赋值为一个空组件
      {
        path: "/chathub",
        name: "chathub",
        component: { render() {} }, // 这里写 null 时控制台会出 warning,提示缺少 render 函数
      },
    
  2. 在 router-view 处,渲染 iframe,通过 v-show 来控制显示隐藏
      <ChatHub v-if="chatHubVisited" v-show="isChatHubPage"></ChatHub>
      <router-view v-slot="{ Component }">
        <keep-alive :include="keepAliveList">
          <component :is="Component"></component>
        </keep-alive>
      </router-view>
    
  3. 监听路由的变化,改变 iframe 的显/隐
    const isChatHubPage = ref(false)
    // 这里是个优化,想的是只有页面访问过该路由才渲染,没访问过就不渲染该组件
    const chatHubVisited = ref(false) 
    
    watch(
      () => routes.path,
      (value) => {
        if (value === '/chathub') {
          chatHubVisited.value = true
          isChatHubPage.value = true
        } else {
          isChatHubPage.value = false
        }
      },
      {
        immediate: true
      }
    )
    
  4. ChatHub.vue组件代码(有单个或者多个iframe情况)
    <template>
      <div class="iframe-container">
        <iframe
          v-for="(item, index) in iframeList"
          v-show="showIframe(item, index)"
          :key="item.url"
          :src="item.url"
          frameborder="0"
        ></iframe>
      </div>
    </template>
    <script lang="ts" setup>
    export default {
      name: "ChatHub",
    };
    import { ref, reactive } from "vue";
    import { useRoute, useRouter } from "vue-router";
    const route = useRoute();
    
    const iframeList = reactive([
      {name: 1, url: "https://xxx"},
      {name: 2, url: "https://yyy"}
    ])
    
    // 是否显示
    const showIframe = (item, index) => {
      if (route.query.url === item.url) {
        return true;
      } else {
        return false;
      }
    };
    
    </script>


http://www.kler.cn/news/308641.html

相关文章:

  • 快速开发与维护:探索 AndroidAnnotations
  • Edegex Foundry docker和源码安装
  • uniapp与webview进行数据通信
  • 每个电脑都有ip地址吗?不同电脑ip地址一样吗
  • 爬虫代理失效怎么处理?全面解决方案
  • 【智路】智路OS 设备接入开发
  • 力扣122.-买卖股票的最佳时机 II(Java详细题解)
  • Python数据分析 Pandas基本操作
  • .NET 6.0 + WPF 使用 Prism 框架实现导航
  • Linux下编译Kratos
  • 如何动态获取路由上的参数
  • 最短路径算法
  • 详解QT元对象系统用法
  • webpack原理简述
  • java实现真值表代码(不完善)恳求大佬指导
  • 利用AI驱动智能BI数据可视化-深度评测Amazon Quicksight(三)
  • 使用 Visual Studio Code 配置 C++ 开发环境的详细指南
  • sqlx1.3.4版本的问题
  • 【MySQL】Windows下重启MySQL服务时,报错:服务名无效
  • 语言模型与人类反馈的深度融合:Chain of Hindsight技术
  • 主流日志框架Logback与Log4j2
  • 【TS】TypeScript配置详解【三】
  • HarmonyOS axios 拦截器处理token 及异常
  • js的书写位置和css的书写位置的区别?为什么要这样写?
  • dedecms(四种webshell姿势)
  • 微服务之间远程调用实现思路
  • pdf文件转图片,base64或保存到本地
  • django 通过地址访问本地文件
  • Java原生HttpURLConnection实现Get、Post、Put和Delete请求完整工具类分享
  • 高级I/O知识分享【5种IO模型 || select || poll】