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的使用.
+++primary 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">{{item}}</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
})
}