在Vue中实现不刷新的iframe页面的方案

 更新时间:2025年01月12日 08:18:14   作者:Luckily_BAI  
在Vue项目中,我们可能会遇到这样的需求:需要在应用中嵌入iframe页面,并且要求在路由切换的过程中,iframe的内容不会被刷新,本文将介绍如何解决这个问题,并给出具体的实现方案,需要的朋友可以参考下

引言

在Vue项目中,我们可能会遇到这样的需求:需要在应用中嵌入iframe页面,并且要求在路由切换的过程中,iframe的内容不会被刷新。实现这一需求时,Vue自带的keep-alive并不适用,因为它的工作原理并不适用于iframe元素。本文将介绍如何解决这个问题,并给出具体的实现方案。

Vue的keep-alive原理

首先,我们需要理解为什么Vuekeep-alive对于iframe不起作用。keep-alive的工作原理是把组件的VNode(虚拟DOM节点)缓存到内存中,在需要渲染时直接从缓存中提取,而不是重新渲染组件。可是,iframe里的内容并不属于Vue的VNode的一部分,因此keep-alive无法保留iframe的状态。当页面切换时,iframe会被重新加载,导致其内容被刷新。

为了实现iframe的内容不刷新的效果,我们需要采取一种不同的方式,避免依赖keep-alive

实现思路

考虑到iframe的状态难以通过keep-alive保存,我想到了一种基于路由切换方式的解决方案。可以利用v-show来切换iframe的显示与隐藏,从而确保iframe始终在页面中存在,而不被销毁。具体思路如下:

  • 非iframe页面:使用Vue的路由切换机制来切换页面内容。
  • iframe页面:通过v-show来控制iframe组件的显示与隐藏,而不让其从DOM中删除。这样,当路由切换时,iframe页面的内容不会被重新加载。

解决方案

我们可以将iframe页面的渲染与Vue的路由机制结合起来,并封装成一个通用的组件。下面是具体的实现步骤。

1. 配置路由

首先,我们在main.js中配置路由,使用一个iframeComponent属性来标识哪些页面是包含iframe的页面。此属性将存储iframe组件的引用。

import Vue from 'vue';
import App from './App.vue';
import VueRouter from 'vue-router';

// 引入需要展示的iframe组件
import F1 from './components/F1.vue';
import F2 from './components/F2.vue';

Vue.use(VueRouter);

// 配置路由
const routes = [
  {
    path: '/f1',
    name: 'f1',
    iframeComponent: F1, // 该页面包含iframe
  },
  {
    path: '/f2',
    name: 'f2',
    iframeComponent: F2, // 该页面包含iframe
  },
  {
    path: '/index',
    component: { template: '<div>Index Page</div>' }
  }
];
​
const router = new VueRouter({
  routes
});
​
new Vue({
  render: h => h(App),
  router
}).$mount('#app');

2. 封装iframe-router-view组件

我们需要一个新的组件来处理iframe页面的显示与隐藏。这个组件会监听路由变化,并根据路由路径动态决定哪些iframe页面应该渲染。

创建iframe-router-view.vue:

<template>
  <div>
    <!-- Vue的router-view -->
    <keep-alive>
      <router-view></router-view>
    </keep-alive>
​
    <!-- 动态渲染iframe页面 -->
    <component
      v-for="item in hasOpenComponentsArr"
      :key="item.name"
      :is="item.name"
      v-show="$route.path === item.path"
    ></component>
  </div>
</template>
​
<script>
import Vue from 'vue/dist/vue.js';
​
export default {
  data() {
    return {
      componentsArr: [] // 存储所有含有iframe的页面
    };
  },
  created() {
    // 获取路由配置中的iframe页面,并注册组件
    const componentsArr = this.getComponentsArr();
    componentsArr.forEach((item) => {
      Vue.component(item.name, item.component);
    });
    this.componentsArr = componentsArr;
    this.isOpenIframePage(); // 检查当前路由是否是iframe页面
  },
  watch: {
    $route() {
      // 路由变化时更新显示的iframe页面
      this.isOpenIframePage();
    }
  },
  computed: {
    // 实现懒加载,只渲染已打开过的iframe页面
    hasOpenComponentsArr() {
      return this.componentsArr.filter(item => item.hasOpen);
    }
  },
  methods: {
    // 判断当前路由是否是iframe页面,并设置`hasOpen`标志
    isOpenIframePage() {
      const target = this.componentsArr.find(item => item.path === this.$route.path);
      if (target && !target.hasOpen) {
        target.hasOpen = true;
      }
    },
    // 获取所有路由配置中含有iframeComponent的页面
    getComponentsArr() {
      const router = this.$router;
      const routes = router.options.routes;
      const iframeArr = routes.filter(item => item.iframeComponent);
      
      return iframeArr.map((item) => {
        const name = item.name || item.path.replace('/', '');
        return {
          name: name,
          path: item.path,
          hasOpen: false, // 是否已打开过
          component: item.iframeComponent // iframe组件引用
        };
      });
    }
  }
};
</script>

3. 更新根组件

在根组件中,替换原本的router-view,使用我们封装的iframe-router-view组件来替代。

<template>
  <div id="app">
    <div class="nav">
      <router-link class="router" to="/f1">Go to F1</router-link>
      <router-link class="router" to="/f2">Go to F2</router-link>
      <router-link class="router" to="/index">Go to Index</router-link>
    </div>

    <!-- 使用新的iframe-router-view组件 -->
    <iframe-router-view></iframe-router-view>
  </div>
</template>

<script>
import F1 from './components/F1';
import F2 from './components/F2';
import IframeRouterView from './components/iframe-router-view.vue';

export default {
  name: 'App',
  components: {
    F1,
    F2,
    IframeRouterView
  }
};
</script>

4. 进一步优化

  • 懒加载:通过hasOpen标志,我们确保只有在用户访问过对应的iframe页面时,iframe组件才会被渲染。这是一个简易的懒加载机制,可以提升性能,避免不必要的资源浪费。
  • 动态注册:我们通过动态生成的组件数组来注册iframe页面,无需每次新增iframe页面时都手动修改根组件或main.js。
  • 在关闭tab或其他业务场景下,移除对应的iframe,防止内存溢出。

动态创建iframe的解决方案

class IframeManager {
  constructor() {
    if (IframeManager.instance) {
      return IframeManager.instance;
    }
    this.iframes = new Map();
    IframeManager.instance = this;
    return this;
  }

  /**
   * 创建 iframe
   * @param {string} id - 唯一标识符 必填
   * @param {string} src - iframe 的 URL 必填
   * @param {Object} styles - 自定义样式  可选
   */
  createIframe(id, src, styles = {}) {
    if (this.iframes.has(id)) {
      const iframe = this.iframes.get(id);
      iframe.style.display = 'block';
      return;
    }
    const iframe = document.createElement('iframe');
    iframe.id = id;
    iframe.src = src;
    iframe.frameBorder = '0';

    const defaultStyles = {
      position: 'absolute',
      top: '113px',
      right: '0',
      width: 'calc(100% - 210px)',
      height: 'calc(100% - 113px)',
      overflowY: 'auto',
      borderRadius: '10px 0 0 10px',
      zIndex: '1000',
      display: 'block',
    };

    Object.assign(iframe.style, { ...defaultStyles, ...styles });
    document.body.appendChild(iframe);
    this.iframes.set(id, iframe);
  }

  /**
   * 隐藏 iframe
   * @param {string} id - iframe 的唯一标识符 必填
   */
  hideIframe(id) {
    const iframe = this.iframes.get(id);
    if (iframe) {
      iframe.style.display = 'none';
    }
  }

  /**
   * 销毁 iframe
   * @param {string} id - iframe 的唯一标识符
   */
  destroyIframe(id) {
    const iframe = this.iframes.get(id);
    if (iframe) {
      iframe.remove();
      this.iframes.delete(id);
    }
  }

  /**
   * 销毁所有 iframe
   */
  destroyAllIframes() {
    this.iframes.forEach((iframe, id) => {
      iframe.remove();
      this.iframes.delete(id);
    });
  }
}
const iframeManager = new IframeManager();
export default iframeManager;

上述代码中,我们采用了单例模式来确保实例唯一,可在多个页面进行统一的管理,页面中的使用不再过多赘述,调用上述方法即可。

结语

通过以上方法,我们实现了一个可以在路由切换时保持iframe内容不刷新的解决方案。我们利用Vue的v-show来控制iframe的显示和隐藏,而非通过重新渲染整个iframe元素来避免刷新。

这种方式不仅简化了代码,还能确保应用的性能与体验不受影响。如果你有更好的优化方法或遇到了其他问题,欢迎与我交流讨论!

以上就是在Vue中实现不刷新的iframe页面的方案的详细内容,更多关于Vue不刷新iframe页面的资料请关注脚本之家其它相关文章!

相关文章

  • 详解vue中使用transition和animation的实例代码

    详解vue中使用transition和animation的实例代码

    这篇文章主要介绍了详解vue中使用transition和animation的实例代码,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-12-12
  • Vue中Table组件Select的勾选和取消勾选事件详解

    Vue中Table组件Select的勾选和取消勾选事件详解

    这篇文章主要为大家详细介绍了Vue中Table组件Select的勾选和取消勾选事件详解,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-03-03
  • Vue.js进阶知识点总结

    Vue.js进阶知识点总结

    给大家分享了关于Vue.js想成为高手的5个总要知识点,需要的朋友可以学习下。
    2018-04-04
  • 大前端代码重构之事件拦截iOS Flutter Vue示例分析

    大前端代码重构之事件拦截iOS Flutter Vue示例分析

    这篇文章主要为大家介绍了大前端代码重构之事件拦截iOS Flutter Vue示例分析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-04-04
  • vue3+vite+ts使用require.context问题

    vue3+vite+ts使用require.context问题

    这篇文章主要介绍了vue3+vite+ts使用require.context问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-05-05
  • Vue3 响应式 API 及 reactive 和 ref 的用法示例详解

    Vue3 响应式 API 及 reactive 和 ref&

    响应式是一种允许以声明式的方式去适应变化的编程范例,这篇文章主要介绍了关于Vue3响应式API及reactive和ref的用法,需要的朋友可以参考下
    2023-06-06
  • Vue中渲染系统模块的实现详解

    Vue中渲染系统模块的实现详解

    想要实现一个简洁版的Mini-Vue框架,应该包含三个模块:分别是:渲染系统模块、可响应式系统模块、应用程序入库模块,本文主要介绍的是渲染系统模块的实现,需要的可以参考一下
    2023-07-07
  • vue Echarts实现仪表盘案例

    vue Echarts实现仪表盘案例

    这篇文章主要为大家详细介绍了vue Echarts实现仪表盘案例,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-03-03
  • 解析Vue2 dist 目录下各个文件的区别

    解析Vue2 dist 目录下各个文件的区别

    本篇文章主要介绍了解析Vue2 dist 目录下各个文件的区别,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-11-11
  • vue 清空input标签 中file的值操作

    vue 清空input标签 中file的值操作

    这篇文章主要介绍了vue 清空input标签 中file的值操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-07-07

最新评论