vue记录滚动位置
1.目的
列表页进入详情页时缓存并记录滚动位置,返回列表页面不重新请求数据并滚动到离开时的位置
2.实现
2.1.单页面
不管是浏览器前后退 还是router-link前后退都生效
<router-view v-slot="{ Component }">
<keep-alive :include="['NewBook']">
<component :is="Component" />
</keep-alive>
</router-view>
// 滚动位置
let scrollTop = 0;
onActivated(() => {
// 调用时机为首次挂载
// 以及每次从缓存中被重新插入时
document.documentElement.scrollTop = scrollTop; // 滚到之前的位置
})
onBeforeRouteLeave((to, from) => {
scrollTop = document.documentElement.scrollTop; // 记住滚动位置
})
2.2 多个或所有页面(浏览器前后退生效)
<router-view v-slot="{ Component }">
<keep-alive :include="['NewBook','SearchResults','Recommend','NewArrival','CatalogList']">
<component :is="Component" />
</keep-alive>
</router-view>
const router = createRouter({
history: createWebHashHistory(),
routes,
scrollBehavior(to,from,savePosition){
console.log(11,savePosition)
return new Promise((resolve, reject) => {
if(savePosition){// 浏览器前进后退按钮切换路由
resolve(savePosition)
}
resolve({ left: 0, top: 0 }) // // 通过<router-link>切换
})
}
});
也可以在路由上设置meta:{keepAlive:true}来判断是否保存滚动位置。
2.3 多个页面(任何回退都生效)
// 实际上这里的keepalive 是为了保存页面的状态比如说筛选 检索结果等
<router-view v-slot="{ Component }">
<keep-alive :include="['NewBook','SearchResults','Recommend','NewArrival','CatalogList']">
<component :is="Component" />
</keep-alive>
</router-view>
路由设置keepAlive:true
{
path: 'newBook',//新书速递-BookBlock组件
name: 'NewBook',
component: () => import('@/views/NewBook.vue'),
meta: {keepAlive:true}
},
// 设置了以下两个方法 滚动位置无论是否使用keepalive都生效 前提是列表数据不会变化
router.beforeEach((to, from, next) => {
// 记录页面滚动位置
if (from.meta.keepAlive) {
store.commit('setScrollTop',window.pageYOffset); //可以使用 document.documentElement.scrollTopt 对页面进行相同的操作(Safari 除外,而应该使用 document.body.scrollTop 代替)
}
next()
})
// 页面滚动到指定位置
router.afterEach((to, from) => {
if (to.meta.keepAlive) {
setTimeout(() => {
window.scrollTo(0,store.state.scrollTop);// 页面滚动到指定位置
}, 50)
} else {
window.scrollTo(0,0);
}
})
2.4 单个元素的滚动
同样 需要设置keepAlive
<router-view v-slot="{ Component }">
<keep-alive :include="['CatalogList']">
<component :is="Component" />
</keep-alive>
</router-view>
<van-tree-select
v-model:main-active-index="activeIndex"
height="calc(100vh - 116px)"
:items="itemList"
@click-nav="clickNav"
ref="treeList"
>
<template #content>
<BookBlock :list="catalogList"/>
</template>
</van-tree-select>
// 滚动位置
const treeList = ref();
let scrollTopTree = 0;
let scrollTopBook = 0;
onActivated(() => {
// 调用时机为首次挂载
// 以及每次从缓存中被重新插入时
treeList.value.$el.children[0].scrollTop = scrollTopTree; // 滚到之前的位置
treeList.value.$el.children[1].scrollTop = scrollTopBook; // 滚到之前的位置
})
onBeforeRouteLeave((to, from) => {
scrollTopTree = treeList.value.$el.children[0].scrollTop; // 记住滚动位置
scrollTopBook = treeList.value.$el.children[1].scrollTop; // 记住滚动位置
})
2.3 终极大法
上面2.3的方法会导致从列表页进入或者后退都会保持列表页的状态,数据不会刷新, 实际上有时候从首页进入列表页的时候需要刷新数据的
因此需要实现从列表页前进到详情页面记录状态和滚动位置,从列表页后退不需要记录
首先设置路由
{
path: 'newBook',
name: 'NewBook',
component: () => import('@/views/NewBook.vue'),
meta: {keepAlive:true, deepth:2} // deepth是用来判断前进后退的
},
设置动态添加keepAlive 这里是记录页面数据状态等
<router-view v-slot="{ Component }">
<keep-alive :include="includeNames" :max="1">
<component :is="Component" />
</keep-alive>
</router-view>
router.beforeEach((to, from) => {
// 下面操作主要是为了解决数据刷新问题
// 如果要to(进入)的页面是需要keepAlive缓存的,把name push进include数组中
if (to.meta.keepAlive) {
!includeNames.value.includes(to.name) && includeNames.value.push(to.name);
}
// 如果 要 form(离开) 的页面是 keepAlive缓存的,
// 再根据 deepth 来判断是前进还是后退
// 如果是后退就去除
if (from.meta.keepAlive && to.meta.deepth < from.meta.deepth) {
const index = includeNames.value.indexOf(from.name);
index !== -1 && includeNames.value.splice(index, 1);
}
})
设置路由钩子 这里是记录滚动位置
// 滚动位置记录
router.beforeEach((to, from, next) => {
// 记录页面滚动位置
if (from.meta.keepAlive) {
store.commit('setScrollTop', window.pageYOffset);
}
next()
})
/**
* 后置钩子
*/
router.afterEach((to,from) => {
if (to.meta.keepAlive && to.meta.deepth < from.meta.deepth ) {
setTimeout(() => {
window.scrollTo(0, store.state.scrollTop);// 页面滚动到指定位置
}, 50)
} else {
window.scrollTo(0, 0);
}
});
2.注意事项
尽量不在App.vue内使用,尽量在指定需要缓存的组件上使用。
尽量使用include和exclude字段,使用store维持需要缓存的组件数组。不使用v-if="includeNames.indexOf($route.name) !== -1",使用v-if会出现很多bug.
由于无法解释的神圣旨意,我们徒然地到处找你;你就是孤独,你就是神秘,比恒河或者日落还要遥远。。。。。。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通