最近遇到一需求,从一个看板页面跳转到其他页面之后再回到看板页面,需要定位到跳转之前的位置。主要用到了vue的keep-alive,功能整体实现难度不大,但是总结一下实现过程中遇到的一些问题。
1.keep-alive简介
官网传送门
2.路由修改
为了区别不同路由的缓存与否,在路由配置中meta添加keepAlive字段,修改App.vue
//router.js
{
path: '/xxx',
name: 'xxx',
component: ()=>{}
meta: {
keepAlive: true //在添加keepAlive属性
}
}
//app.vue
<!--会被缓存的路由-->
<keep-alive>
<router-view v-if="$route.meta.keepAlive"></router-view>
</keep-alive>
<!--不会被缓存的路由-->
<router-view v-if="!$route.meta.keepAlive"></router-view>
3.注意事项
使用keep-alive包裹的路由会多两个钩子函数deactived(被 keep-alive 缓存的组件停用时调用),actived(被 keep-alive 缓存的组件激活时调用)。另外使用keep-alive包裹的组件只会调用一次created,mounted等钩子函数,如果在路由更改之后请求数据务必在actived钩子函数中重新请求数据。
4.缓存页面高度实践
缓存页面高度指导思想,在deactived钩子函数执行的时候缓存页面滚动条高度,在actived时动态设置页面滚动条高度。在给出的代码示例中,假设在main-container中引入了两个组件,两个组件的高度比较高,超出了一屏内容。window._.debounce使用的是lodash.js方法。由于代码结构遗留问题,页面超出显示滚动条仅显示在main-container容器中。
...
//离开页面,记录容器滚动条高度
deactivated () {
this.scrollY = this.$refs['mainDom'].scrollTop
},
//进入页面,设置容器滚动条高度
activated() {
this.$refs['mainDom'].scrollTo({top: this.scrollY,left: 0})
}
...
误区:
页面滚动之后,使用document.getElementsByClassName获取DOM,打印为空。原因是执行到deactivated钩子时页面已经跳转,此时的document已经不再是当前页面,故获取的DOM不存在。
在deactivated后发现执行到这一步的时候this实例指向的还是当前页面,遂使用this.$refs获取,打印出来scrollTop为0(这里没有搞明白,已经滚动但是无数据,可能是dom元素产生了一些变化)
替代方案
在mounted的时候就监听滚动事件,动态的设置scrollY,在activated的时候设置上去。
mounted(){
this.$refs['mainDom'].addEventListener('scroll', window._.debounce(() => {
this.scrollY = this.$refs['mainDom'].scrollTop
},150), false)
},
追加需求
如果B路由设置了路由缓存,由B路由回到上级A路由不想缓存路由,由B路由跳转到下级C路由想缓存路由。实现功能也很简单,在路由钩子函数中动态设置meta里面的keepAlive值就OK了。
beforeRouteLeave(to, from, next) {
const {name} = to
if(name == 'A路由名字'){
from.meta.keepAlive = false
}else{
from.meta.keepAlive = true
}
next();
}
完整代码
<template>
<div class="main-container" ref="mainDom">
<template1 ref="template1 "/>
<template2 ref="template2 "/>
</div>
</template>
<script>
import template1 from 'xxx/xxx/template1.vue'
import template2 from 'xxx/xxx/template2.vue'
export default {
components:{
template1, template2
},
beforeRouteLeave(to, from, next) {
const {name} = to
if(name == 'A路由名字'){
from.meta.keepAlive = false
}else{
from.meta.keepAlive = true
}
next();
},
data(){
return{
scrollY:0
}
}
mounted(){
this.$refs['mainDom'].addEventListener('scroll', window._.debounce(() => {
this.scrollY = this.$refs['mainDom'].scrollTop
},150), false)
},
deactivated () {
},
activated() {
this.$refs['mainDom'].scrollTo({top: this.scrollY,left: 0})
}
}
</script>
<style lang="scss" scoped>
.main-container{
height: 100vh;
background: #EEEEED;
overflow-y: scroll;
box-sizing: border-box;
padding-top: 0.6rem;
...
}
</style>