Vue中对iframe实现keep alive无刷新的方法


Posted in Javascript onJuly 23, 2019

前言

最近一个需求,需要在 Vue 项目中加入 含有iframe 的页面,同时在路由切换的过程中,要求iframe的内容 不会被刷新 。一开始使用了Vue自带的keep- alive发现没有用,于是自己研究了一下解决方案。。。。。。

Vue的keep-alive原理

要实现对保持iframe页的状态。我们先搞清楚为什么Vue的keep-alive不能凑效。keep-alive原理是把组件里的节点信息保留在了 VNode (在内存里),在需要渲染时候从Vnode渲染到真实DOM上。iframe页里的内容并不属于节点的信息,所以使用keep-alive依然会重新渲染iframe内的内容。 另外 ,我也尝试有过想法:如果把整个iframe节点保存起来,然后需要切换时把它渲染到目标节点上,能否实现iframe页不被刷新呢?————也是不可行的,iframe每一次渲染就相当于打开一个新的网页窗口,即使把节点保存下来,在渲染时iframe页还是刷新的。

实现的思路

既然保持iframe页里的状态很难实现,在这个时候我想到了一个别的方法。能否在Vue的route-view节点上动点手脚?使得在切换 非iframe页 的时候使用Vue的路由,当切换 iframe页 时则使用 v-show 切换显示与隐藏,使得iframe节点 一直不被删除 ,这样就能保持iframe的状态了。

我们简陋的实现一下以上的效果,上代码:

入口main.js:

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

const Index = { template: '<div>Index</div>' }
const routes = [
 // 含有iframe的两个页面
 {
 path: '/f1',
 name: 'f1'
 },
 // 含有iframe的两个页面
 {
 path: '/f2',
 name: 'f2'
 },
 {
 path: '/index',
 component: Index
 }
]

const router = new VueRouter({
 routes
});

Vue.use(VueRouter);
new Vue({
 render: h => h(App),
 router
}).$mount('#app')

根组件:

<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>
 
 <keep-alive>
  <!-- Vue的路由 -->
  <router-view></router-view>
 </keep-alive>
 
 <!-- iframe页面 -->
 <f1 v-show="$route.path == '/f1'"></f1>
 <f2 v-show="$route.path == '/f2'"></f2>
 </div>
</template>

<script>
import F1 from './components/f1';
import F2 from './components/f2';
export default {
 name: 'app',
 components: {
 F1,
 F2
 },
 
}
</script>

上面代码简单来说,关键的地方首先是main.js初始化路由时,对iframe页不填写属性component,这样页面就是空白的。然后在 router-view 节点旁边渲染iframe页组件,使用$route.path判断当前路由的指向,控制iframe页的 显示与隐藏 。

上面代码简单的解决了问题,但还有一些地方可以优化:

  1. iframe页在根节点App.vue一渲染时 已经渲染 了,对此iframe页可以做成 懒加载 ,只有在进入过相应页面了触发渲染,并且渲染过之后就用v-show切换显示与隐藏
  2. 每当增加一个iframe页都要增加一段的组件引入注册和调用的代码。比较 繁琐 。我们目标应该做到每增加一个iframe页,只需要添加尽量少的代码。这里思路是:
    1. 在路由配置中定义一个属性,用于 标识该页面是否含有iframe 的页面
    2. 根据标识,iframe页组件 自动动态注册和渲染 ,无需再手写额外的代码
    3. router-view和iframe切换的逻辑封装成 新组件 ,用它 替代原有的router-view

我们先修改router的配置,增加一个属性名iframeComponent,用于标识是否包含iframe,该属性的值是组件文件引用

main.js:

import F1 from './components/f1';
import F2 from './components/f2';

const routes = [
 {
 path: '/f1',
 name: 'f1',
 iframeComponent: F1 // 用于标识是否含有iframe页
 },
 {
 path: '/f2',
 name: 'f2',
 iframeComponent: F2 // 用于标识是否含有iframe页
 },
 {
 path: '/index',
 component: { template: '<div>Index</div>' }
 }
]

const router = new VueRouter({
 routes // (缩写)相当于 routes: routes
});

new Vue({
 render: h => h(App),
 router
}).$mount('#app')

接下来我们第二步和第三步结合在一起,封装新的组件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 {
 created() {
  // 设置iframe页的数组对象
  const componentsArr = this.getComponentsArr();
  componentsArr.forEach((item) => {
   Vue.component(item.name, item.component);
  });
  this.componentsArr = componentsArr;
  // 判断当前路由是否iframe页
  this.isOpenIframePage();
 },
 data() {
  return {
   componentsArr: [] // 含有iframe的页面
  }
 },
 watch: {
  $route() {
   // 判断当前路由是否iframe页
   this.isOpenIframePage();
  }
 },
 computed: {
  // 实现懒加载,只渲染已经打开过(hasOpen:true)的iframe页
  hasOpenComponentsArr() {
   return this.componentsArr.filter(item => item.hasOpen);
  }
 },
 methods: {
  // 根据当前路由设置hasOpen
  isOpenIframePage() {
   const target = this.componentsArr.find(item => {
    return 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, // 是否打开过,默认false
     component: item.iframeComponent // 组件文件的引用
    };
   });
  }
 }
}
</script>
  1. 该组件主要做的是根据main.ja里的routes生成一个只含有iframe页的数组对象。
  2. watch上监听$route,判断当前页面在iframe页列表里的话就设置hasOpen属性为true,渲染该组件
  3. 用v-show="$route.path === item.path"切换iframe页的显示与隐藏。

逻辑并不复杂,这里就不多赘述。

结语

大家如果有更好的实现方法,或者我上面还有什么需要更正的错误,欢迎交流。 上面demo的代码放在了个人github上 https://github.com/jmx164491960/vue-iframe-demo

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
学习ExtJS(一) 之基础前提
Oct 07 Javascript
基于PHP+Jquery制作的可编辑的表格的代码
Apr 10 Javascript
JS操作select下拉框动态变动(创建/删除/获取)
Jun 02 Javascript
javascript实现日期按月份加减
May 15 Javascript
jQuery ajax调用后台aspx后台文件的两种常见方法(不是ashx)
Jun 28 Javascript
jQuery实现的简单悬浮层功能完整实例
Jan 23 Javascript
JavaScript数据结构之单链表和循环链表
Nov 28 Javascript
Vue使用mixins实现压缩图片代码
Mar 14 Javascript
Vue自定义toast组件的实例代码
Aug 15 Javascript
echarts大屏字体自适应的方法步骤
Jul 12 Javascript
微信小程序中网络请求缓存的解决方法
Dec 29 Javascript
Vue跨域请求问题解决方案过程解析
Aug 07 Javascript
jQuery Ajax async=&gt;false异步改为同步时,解决导致浏览器假死的问题
Jul 22 #jQuery
这应该是最详细的响应式系统讲解了
Jul 22 #Javascript
20道JS原理题助你面试一臂之力(必看)
Jul 22 #Javascript
微信小程序webview 脚手架使用详解
Jul 22 #Javascript
koa2 用户注册、登录校验与加盐加密的实现方法
Jul 22 #Javascript
koa2服务端使用jwt进行鉴权及路由权限分发的流程分析
Jul 22 #Javascript
在小程序中推送模板消息的实现方法
Jul 22 #Javascript
You might like
php文件上传你必须知道的几点
2015/10/20 PHP
PHP生成(支持多模板)二维码海报代码
2018/04/30 PHP
详解Laravel服务容器的绑定与解析
2019/11/05 PHP
juqery 学习之五 文档处理 包裹、替换、删除、复制
2011/02/11 Javascript
javascript实现获取cookie过期时间的变通方法
2014/08/14 Javascript
js使用setTimeout实现定时炸弹的方法
2015/04/10 Javascript
jquery validate demo 基础
2015/10/29 Javascript
很实用的js选项卡切换效果
2016/08/12 Javascript
JS for...in 遍历语句用法实例分析
2016/08/24 Javascript
JavaScript Date 知识浅析
2017/01/29 Javascript
IntersectionObserver实现图片懒加载的示例
2017/09/29 Javascript
原生JS+CSS实现炫酷重力模拟弹跳系统的登录页面
2017/11/01 Javascript
vue-cli系列之vue-cli-service整体架构浅析
2019/01/14 Javascript
vue cli3.0结合echarts3.0与地图的使用方法示例
2019/03/26 Javascript
js对象数组和对象的使用实例详解
2019/08/27 Javascript
解决layui数据表格排序图标被超出的表头挤出去的问题
2019/09/19 Javascript
Vue-cli打包后部署到子目录下的路径问题说明
2020/09/02 Javascript
Django框架中的对象列表视图使用示例
2015/07/21 Python
12步入门Python中的decorator装饰器使用方法
2016/06/20 Python
Python中取整的几种方法小结
2017/01/06 Python
详解Django解决ajax跨域访问问题
2018/08/24 Python
解决Pandas的DataFrame输出截断和省略的问题
2019/02/08 Python
python3获取url文件大小示例代码
2019/09/18 Python
多视角3D逼真HTML5水波动画
2016/03/03 HTML / CSS
英国音乐设备和乐器商店:Gear4music
2017/10/16 全球购物
意大利高端时尚买手店:Stefania Mode
2018/03/01 全球购物
毕业实习个人鉴定范文
2013/12/10 职场文书
工程管理专业毕业生自荐信
2014/01/24 职场文书
后备干部考察材料
2014/02/12 职场文书
工程技术员岗位职责
2014/03/02 职场文书
《生命的药方》教学反思
2014/04/08 职场文书
党的群众路线教育实践活动个人对照检查材料(校长)
2014/11/05 职场文书
异地恋情人节寄语
2015/02/28 职场文书
Mysql中存储引擎的区别及比较
2021/06/04 MySQL
如何避免mysql启动时错误及sock文件作用分析
2022/01/22 MySQL
Windows和Linux上部署Golang并运行程序
2022/04/22 Servers