vue keep-alive组件使用探究
最近在使用vue的keep-alive组件实现页面缓存的时候遇到了一些问题
最主要的困惑是没有分清meta标签中的keeAlive的true和false值的影响,以为能控制keep-alive组件是否缓存页面,后面发现我想错了。
keep-alive只是一个组件,用来缓存页面,它本事是很机械的。
- 不管使用这种
<keep-alive>
<router-view v-if="$route.meta.keepAlive"></router-view>
</keep-alive>
<router-view v-if="!$route.meta.keepAlive"></router-view>
2.还是这种
<keep-alive include="c">
<router-view></router-view>
</keep-alive>
本质上是一样的,直接在开始的时候定义好了哪些页面需要被缓存。这就会出现一个问题,当一个页面需要动态判断是否需要被缓存时就会无法实现。
比如,从A入口页面到B列表页需要刷新页面,从B滚动浏览了某个tab分类下的商品点击进入C详情页希望被缓存。这就需要B页面被缓存,这时候B页面的缓存时需要根据路由场景动态判断的。这一点是include
和v-if="$route.meta.keepAlive"
无法实现的。
解决方案
- 通过在B列表页判断页面来源,使用actived和created判断进入页面的来源和次数,进而确定是否要调用函数重载页面。实现思路是一定缓存B页面,但是在A页面进入时调用函数发送ajax更新数据和视图。
created() {
// console.log('proall','--created')
this.isFirst = false
this.showTopBar(true)
},
activated () {
// console.log('activated')
const prevpage = this.$store.state.prevpage
// 从首页二次进来需要重载
if (prevpage === 'baopro' && this.isFirst) {
this.reload()
// 其他页面返回,无需重载,返回锚点
} else {
// 这里时回滚记录的页面高度,因为scrollBehavior钩子无效才使用该方式
document.getElementById('baopro').scrollTop = this.$store.state.topDistance
}
}
2.通过在B列表页判断页面去向,使用beforeRouteLeave钩子返回A页面的时候,就把所有的页面缓存清掉。实现思路是一定缓存B页面,但是返回A页面的时候清除缓存,这样A重新进入B就会重载。
beforeRouteLeave(to,from,next) {
if (to.name === 'a') {
this.$vnode.parent.componentInstance.cache = {}
this.$vnode.parent.componentInstance.keys = []
}
next()
}
我更倾向于第二种实现,因为更简洁,附上代码。
- router.js
{
path: '/a',
name: 'a',
component: a,
props: true,
meta: {
keepAlive: false
}
},{
path: '/b',
name: 'b',
component: b,
props: true,
meta: {
keepAlive: true
}
},
{
path: '/c',
name: 'c',
component: c,
props: true,
meta: {
keepAlive: false
}
}
- App.vue
<template>
<div id="app">
<keep-alive>
<router-view v-if="$route.meta.keepAlive"></router-view>
</keep-alive>
<router-view v-if="!$route.meta.keepAlive"></router-view>
// 这里是方式一用来查看meta的keepAlive属性的值的
<div>{{String($route.meta.keepAlive)}}</div>
</div>
</template>
- a.vue
<template>
<div class="fa">
<button @click="$router.push('b')">去B</button>
</div>
</template>
<style lang="scss" scoped>
button {
width: 50px;
height: 30px;
margin-top: 40px;
margin-right: 40px;
}
</style>
- b.vue
<template>
<div class="fa">
<div class="inner" :style="{background:item.bg}" v-for="item in list" :key="item.id" @click="go(item)">
</div>
<button @click="$router.back()">回A</button>
<button @click="$router.push('c')">去C</button>
</div>
</template>
<script>
export default {
name:'B',
data () {
return {
list:[
{bg:'yellow',id:0},
{bg:'yellow',id:1},
{bg:'yellow',id:2},
]
}
},
beforeRouteLeave(to,from,next) {
if (to.name === 'a') {
this.$vnode.parent.componentInstance.cache = {}
this.$vnode.parent.componentInstance.keys = []
}
next()
},
methods: {
go(item) {
item.bg = item.bg === 'red' ? 'yellow' : 'red'
}
}
}
</script>
<style lang="scss" scoped>
.inner {
width: 100%;
height: 100px;
margin-top: 50px;
/* background-color: yellow; */
}
.inner > span {
font-size: 30px;
}
button {
width: 50px;
height: 30px;
margin-top: 40px;
margin-right: 40px;
}
</style>
- c.vue
<template>
<div class="fa" >
<p>这是页面C</p>
<button @click="$router.back()">点击返回B</button>
</div>
</template>
<style lang="scss" scoped>
.fa {
font-size: 40px;
}
</style>