keep-alive缓存不了iframe

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

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

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

我们先了解下 KeepAlive

KeepAlive (熟悉的可跳过本节)

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

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

javascript 复制代码
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 赋值为一个空组件

    javascript 复制代码
      {
        path: "/chathub",
        name: "chathub",
        component: { render() {} }, // 这里写 null 时控制台会出 warning,提示缺少 render 函数
      },
  2. 在 router-view 处,渲染 iframe,通过 v-show 来控制显示隐藏

    javascript 复制代码
      <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 的显/隐

    javascript 复制代码
    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情况)

    javascript 复制代码
    <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>
相关推荐
崔庆才丨静觅10 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby606111 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了11 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅11 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅12 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅12 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment12 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅13 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊13 小时前
jwt介绍
前端
爱敲代码的小鱼13 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax