vant list 组件滚动保留滚动条位置,需结合 keepAlive 使用
# 需求
1. 现有一个列表界面 page1,列表详情界面 page2。
2. 先从列表界面 page1 进入到列表详情界面 page2,然后从 page2 回到 page1 之后,列表界面 page1 的位置不刷新(即回到原来的浏览位置)
# 实现方法
1、保存位置的前提是用的 keepAlive
组件来做缓存, app.vue
代码
<template> | |
<div id="app"> | |
<keep-alive> | |
<router-view v-if='$route.meta.keepAlive'/> | |
</keep-alive> | |
<router-view v-if='!$route.meta.keepAlive'/> | |
</div> | |
</template> |
通过 V-if
进行判断,如果前面路由配置的 $route.meta.keepAlive
为 true
,则会将组件进行缓存,因此我们的列表界面的 keepAlive
需要设置为 true
。
- 不使用
keep-alive
时,钩子函数执行顺序为:beforeRouteEnter --> created --> mounted --> destroyed
- 使用
keep-alive
缓存组件时,钩子函数执行顺序为:beforeRouteEnter --> created --> mounted --> activated --> deactivated
再次进入缓存的页面,只会触发beforeRouteEnter -->activated --> deactivated
。created
和mounted
不会再执行。
# 方法一
1、在路由文件 router.js
,给每个路由 meta 添加 scrollTop
和 keepAlive
{ | |
path: '/home', | |
name: 'home', | |
component: resolve => require(['@/views/home/index.vue'], resolve), | |
meta: { | |
title: '首页', | |
index: 1, | |
keepAlive: true, | |
scrollTop: 0 | |
} | |
}, | |
{ | |
path: '/classify', | |
name: 'classify', | |
component: resolve => require(['@/views/classify/index.vue'], resolve), | |
meta: { | |
title: '分类', | |
index: 1, | |
keepAlive: true, | |
scrollTop: 0 | |
} | |
}, | |
{ | |
path: '/shopping', | |
name: 'shopping', | |
component: resolve => require(['@/views/shopping/index.vue'], resolve), | |
meta: { | |
title: '购物车', | |
index: 1, | |
keepAlive: true, | |
scrollTop: 0 | |
} | |
}, | |
{ | |
path: '/detail', | |
name: 'detail', | |
component: resolve => require(['@/views/detail/index.vue'], resolve), | |
meta: { | |
title: '详情', | |
index: 2, | |
// keepAlive: true, | |
// scrollTop: 0 | |
} | |
}, |
2、然后在 main.js
, 记录滚动条的位置
router.beforeEach((to, from, next) => { | |
if (from.meta.keepAlive) { | |
const $wrapper = document.querySelector('.app-wrapper'); // 列表的外层容器 注意找到滚动的盒子 | |
const scrollTop = $wrapper ? $wrapper.scrollTop : 0; | |
console.log('scrollTop=', scrollTop) | |
from.meta.scrollTop = scrollTop; | |
} | |
next(); | |
}); |
3、最后在需要记录保留滚动条位置的地方获取通过 activated(这个函数每次进入页面都会执行,只有结合使用 keepAlive
组件才有效)来获取 scrollTop
activated () { | |
const scrollTop = this.$route.meta.scrollTop; | |
const $wrapper = document.querySelector('.app-wrapper'); | |
if (scrollTop && $wrapper) { | |
$wrapper.scrollTop = scrollTop; | |
} | |
}, |
比如缓存了某些页面也不想随之滚动,则把 scrollTop 置 0 即可;
activated() { | |
const $wrapper = document.querySelector(".app-wrapper"); | |
$wrapper.scrollTop = 0; | |
}, |
注意,页面滚动的话,其他页面有滚动条的也会随之滚动,可以对其他页面里面处理,或者判断是否从详情页到列表页来判断是否缓存位置,如果不是,则回到顶部,但是注意路由钩子函数 this 的使用.
issue
当 route 开启 reactive 缓存时,van-tabs 的转场动画 animated 属性会导致 van-tab 永远指向 name="0"
如果不定义 name,则使用 keep-alive 会直接去取文件名,也就是 index,发生错误,keep-alive 缓存无效。
# 方法二
# 一、先缓存列表界面
1. 先在路由管理文件 index.js 中添加 meta 属性
{ | |
path: '/datadetail', | |
component: DataDetail | |
}, | |
{ | |
path: '/datalist', | |
component: DataList, | |
// 设置 keepAlive:true--- 说明此组件需要进行数据缓存 | |
meta: { | |
keepAlive: true | |
} | |
}, |
# 二、获取下拉列表的位置
1. 先在 page1.vue 列表详情组件中找到下拉列表的 div
并设置 ref
属性
<div class="wrapper" ref="wrapper"> | |
<div class="title">我是标题</div> | |
<van-pull-refresh v-model="isRefresh" @refresh="onRefresh"> | |
<van-list class="list" v-model="loadingMore" :finished="finished" finished-text="没有更多了" @load="onLoadMore"> | |
<div class="item-wrapper" v-for="item in list" :key="item.id" @click="clickItem(item)"> | |
<div class="item"><!--swig0--></div> | |
</div> | |
</van-list> | |
</van-pull-refresh> | |
</div> |
在上面的下拉列表中设置 ref="wrapper"
的属性
2. 因为使用了 keep-alive
,页面被缓存起来了,所以 data 里的数据不会丢失,可以在 data
中声明一个变量 scroll
来存储 scrollTop
的值。
data() { | |
return { | |
scroll: 0,// 存储 `scrollTop` 的值 | |
} | |
}, | |
// 离开路由之前执行的函数 | |
beforeRouteLeave(to, from, next) {...}, |
3. 然后再 page1.vue 的页面中添加一个钩子函数 beforeRouteLeave(to, from, next)
// 离开路由之前执行的函数 | |
beforeRouteLeave(to, from, next) { | |
// 如果在 window 中出现的滚动条 | |
// this.scroll = window.scrollTop; | |
// 如果在某个指的元素中出现的滚动条 就在该素中添加 ref 属性(例如上面的 div 设置 ref="wrapper") | |
this.scroll = this.$refs.wrapper.scrollTop; | |
next() | |
}, |
# 三、获取并设置 scrollTop
通过 beforeRouteLeave(to, from, next)
来获取的列表位置值,并将位置值存储到 scroll
中,从 page2 页面返回到列表页面 page1 时,获取前面缓存的列表高度 scroll
值,并赋值给 scrollTop
,从而达到返回列表时位置不变,只需要再 activated
钩子函数中设置 scrollTop
,就可实现需求。
data() { | |
return { | |
scroll: 0,// 存储 `scrollTop` 的值 | |
} | |
}, | |
// 离开路由之前执行的函数 | |
beforeRouteLeave(to, from, next) {...}, | |
// 这一步就能实现需求 | |
activated() { | |
this.$refs.wrapper.scrollTop = this.scroll | |
} |
当然也有的说是,将 scroll
赋值的时候,直接赋值在进入路由之前执行的钩子函数中 beforeRouteEnter(to, from, next)
,但是这个我没有实现,也可以参考一下:
data() { | |
return { | |
scroll: 0,// 存储 `scrollTop` 的值 | |
} | |
}, | |
// 离开路由之前执行的函数 | |
beforeRouteLeave(to, from, next) {...}, | |
// 进入路由之前执行的函数 | |
beforeRouteEnter(to, from, next) { | |
next(vm => { | |
// 如果在 window 中出现的滚动条 | |
// window.scrollTop = vm.scroll; | |
// 如果在某个指的元素中出现的滚动条 就在该素中添加 ref 属性,如:ref="listBox" | |
vm.$refs.listBox.scrollTop = vm.scroll | |
}) | |
} |