最近工作中,项目上遇到一个这样的需求,就是在打开报表的打印预览界面时,去切换标签,切回打印预览界面的时候,要求界面不刷新。vue框架中,我们去处理此类问题,通常马上就会想到去使用vue框架中自带的keep-alive组件,所以一开始我也是去使用了keep-alive,但是发现没有达到预期效果,后面通过研究和查阅资料发现,在vue项目中加入了含有iframe的页面,在路由切换的过程中,使用keep-alive是达不到数据缓存的效果的。
(1)vue中的keep-alive
【1】原理:Vue 的缓存机制并不是直接存储 DOM 结构,而是将 DOM 节点抽象成了一个个 VNode节点。因此,Vue 的 keep-alive 缓存也是基于 VNode节点 而不是直接存储 DOM 节点。在需要渲染的时候从Vnode渲染到真实DOM上。
【2】参数:Keep-alive 组件提供了 include 和 exclude 两个属性,允许组件有条件的进行缓存。
include: 字符串或正则表达式。只有匹配的组件会被缓存。
exclude: 字符串或正则表达式。任何匹配的组件都不会被缓存。
【3】Keep-alive 组件提供了两个生命钩子函数,分别是 activated 和 deactivated 。
activated :当页面存在缓存的时候执行该函数。
deactivated :在页面结束时触发该方法,可清除掉滚动方法等缓存。
(2)iframe中keep-alive机制失效原因:iframe页里的内容并不属于节点的信息,所以使用keep-alive依然会重新渲染iframe内的内容。而且iframe每一次渲染就相当于打开一个新的网页窗口,即使把节点保存下来,在渲染时iframe页还是刷新的。
(1)保存iframe页里的节点信息我们很难去操作,这个时候我们就该想是否能在vue的route-view节点上动些手脚。
(2)我们可以在切换不含iframe的界面时使用vue路由,在切换含iframe页的界面时利用v-show来控制显示隐藏,使iframe的节点不被删除,以此来防止界面节点被重新更新,从而达到保存iframe节点数据的效果。
具体该怎么操作呢?请继续往下看:
第一步:我们先需要修改一下路由配置,如下:
path
:
'/printReportShow'
,
component
:
Layout
,
redirect
:
'/printReportShow'
,
hidden
:
false
,
name
:
'Layout'
,
children
:
[
path
:
'/printReportShow'
,
iframeComponent
:
printReportShow
,
name
:
'printReportShow'
,
hidden
:
false
,
meta
:
{
title
:
'打印预览'
,
}
从以上代码,不难看出/printReportShow就是我们要跳转的路由地址,我们可以看到printReportShow的路由配置中组件我写的是
iframeComponent
而非官方教程中的component,
这样造成的后果就是在 router-view 节点中渲染时,页面显示的是空白页
。
第二步:修改主界面AppMain.vue(你的可能不叫这个)中的template,添加vue的内置组件
component
,如下:
<template>
<section class="app-main">
<transition name="move" mode="out-in" @enter="onEnter">
<keep-alive :include="cachedViews">
<router-view :key="key" />
</keep-alive>
</transition>
<!--iframe页-->
<component
v-for="item in hasOpenComponentsArr"
:key="item.children[0].name"
:is="item.children[0].name"
v-show="$route.path === item.path"
></component>
</section>
</template>
从以上代码我们可以看到,iframe节点数据在根节点App.vue渲染时,也随即跟着渲染了,但是考虑到性能原因,对此我们选择将iframe的显示做成懒加载形式的,只有在用户进入相应的页面时才触发渲染,在渲染完毕后再通过v-show去控制界面在切换时的显示与隐藏。
第三步:AppMain.vue中具体的逻辑处理
<script>
import Vue from 'vue';
export default {
name: 'AppMain',
computed: {
hasOpenComponentsArr() {
return this.componentsArr.filter(item => {
return item.children[0].hasOpen
});
data() {
return {
componentsArr: []
watch: {
$route() {
this.isOpenIframePage();
created() {
const componentsArr = this.getComponentsArr();
componentsArr.forEach((item) => {
Vue.component(item.children[0].name, item.children[0].component);
});
this.componentsArr = componentsArr;
this.isOpenIframePage();
methods:{
isOpenIframePage() {
const target = this.componentsArr.find(item => {
return item.path === this.$route.path
});
if (target && !target.children[0].hasOpen) {
target.children[0].hasOpen = true;
getComponentsArr() {
const router = this.$router;
const routes = router.options.routes;
const iframeArr = []
const singleRoutes = routes[6];
const printReportObj = {
name: routes[6].children[0].name,
path: routes[6].children[0].path,
hasOpen: false,
component: routes[6].children[0].iframeComponent
singleRoutes.children.splice(0,1);
singleRoutes.children.push(printReportObj);
iframeArr.push(singleRoutes);
return iframeArr
</script>
备注:由于我们系统中就涉及到一个iframe界面,所以在处理上可能有些地方写死了,如果时多iframe的可以自行参考着做相应的修改。
由于切换标签,含iframe的component组件不会再触发路由钩子函数和生命周期函数,导致界面数据无法做更新操作,同时浏览器刷新时,界面数据会有丢失的问题,所以我们可以考虑去监听sessionStorage的值,以此来更新数据。详情可以看下面这篇文章:vue中如何实现对sessionStorage的监听。
在这里要特别感谢jmx164491960老哥的提供的demo,感兴趣的小伙伴可以点击此处:git-vue中实现iframe页面的数据缓存,给予了很大的帮助。
1、序言最近工作中,项目上遇到一个这样的需求,就是在打开报表的打印预览界面时,去切换标签,切回打印预览界面的时候,要求界面不刷新。vue框架中,我们去处理此类问题,通常马上就会想到去使用vue框架中自带的keep-alive组件,所以一开始我也是去使用了keep-alive,但是发现没有达到预期效果,后面通过研究和查阅资料发现,在vue项目中加入了含有iframe的页面,在路由切换的过程中,使用keep-alive是达不到数据缓存的效果的。2、使用keep-alive缓存不了iframe界面原因(1)
最近一个需求,需要在 Vue 项目中加入 含有iframe 的页面,同时在路由切换的过程中,要求iframe的内容 不会被刷新 。一开始使用了Vue自带的keep- alive发现没有用,于是自己研究了一下解决方案。。。。。。
Vue的keep-alive原理
要实现对保持iframe页的状态。我们先搞清楚为什么Vue的keep-alive不能凑效。keep-alive原理是把组件里的节点信息保留在了 VNode (在内存里),在需要渲染时候从Vnode渲染到真实DOM上。iframe页里的内容并不属于节点的信息,所以使用keep-alive依然会重新渲染iframe内的内容。 另外
2.这个公共组件注册共享一个路由;
原先解决思路:使用keep-alive标签做缓存,结果公共组件是缓存了,但每次切换加载iframe页面都被刷新了。
最后借鉴了位仁兄的解决思路(https://blog.csdn.net/saowen163/article/details/106318374?utm_medium=distribute.pc_aggpage_search_result.non
1、index页面 主要代码
<keep-alive :include="keepAliveList">
<router-view class="avue-view" v-if="keepAlive"/>
</keep-alive>
provide() {
return {
index: this,
routerRefresh: this.routerRefresh
data() {
return
在keep-alive标签内部添加
include:字符串或正则表达式。只有匹配的组件会被缓存
exclude: 字符串或正则表达式。任何匹配的组件都不会被缓存。
结合router缓存部分页面:
比较实用的例子:
思路:通过beforeRouterLeave这个钩子来对路由里面的keepAlive进行赋值。从而动态的确定A页面是否需要被缓存。
结合router缓存部分页面:
比较实用的例子:
思路:通过b
这个不是业务的要求,但是看到每次进入页面就重新渲染DOM然后再获取数据更新DOM,觉得作为一个前端工程师有必要优化下的加载逻辑,正好vue提供了keep-alive的功能,所以就试用了下。当然,干任何事儿都不会一帆风顺的,在路上的磕磕碰碰在所难免,故在此记录下遇到的问题,希望看到这篇文章的人能有所帮助。ps:这个也没多难。
HTML部分:
<template>
<div class=app>
<router></router>
</keep>
</template>
JavaScript部分:
(1)方案一、vue项目中,我们可以使用element-resize-detector插件来实现,简单粗暴。实现方案如下:
1、安装相关插件,npm install element-resize-detector
2、在main.js中引入依赖,并挂在vue原型链上:
import ElementResizeDetectorMaker f
1、
使用场景
(1)在
vue项目中,为了在
浏览器刷新时,页面数据不丢失,我们
会选择将数据
浏览器缓存sessionStorage
中。
(2)为了传递参数
之所以写这篇文章,也是在上次
处理vue中使用keep- a
live缓存机制,
切换标签含有iframe标签的
界面会被
重新刷新的
问题
过程
中,由于
切换标签,含
iframe的组件不
会在触发路由钩子函数和生命周期函数,导致
界面数据无法做更新操作,同时还要解决
浏览器刷新时,
界面数据不丢失的
问题,所以才考虑到去监听sessionStorage的值,以此来更新数据。
保留keep-alive可以让Vue组件在切换时不会被销毁,从而保持组件的状态和数据。如果想让iframe不刷新,可以使用Vue的<keep-alive>标签将iframe包裹起来,这样在切换路由时,<keep-alive>标签会保留iframe的状态,从而避免刷新。具体实现步骤如下:
1. 在路由配置文件中,给需要保留的路由添加 meta:{keepAlive:true} 属性,表示该路由需要被缓存。
2. 在App.vue文件中,使用<keep-alive>标签将<router-view>包裹起来,并设置include属性,将需要缓存的路由组件名称添加到include数组中。
3. 在需要保留状态的组件中,可以使用activated和deactivated生命周期函数,来捕获组件被激活和停用的事件,从而在缓存和激活时分别进行相应的处理,以保持组件的状态和数据。
```html
<template>
<keep-alive :include="cachedViews">
<router-view></router-view>
</keep-alive>
</template>
<script>
export default {
name: 'App',
computed: {
cachedViews() {
return this.$store.state.tagsView.cachedViews
</script>
在组件内部,可以使用activated和deactivated生命周期函数来处理缓存和激活时需要进行的操作,例如:
```js
export default {
name: 'MyComponent',
activated() {
// 组件被缓存时调用
deactivated() {
// 组件被激活时调用