Vue 3 自定义 Hook:实现页面数据刷新与滚动位置还原
存在一个场景:
在列表页滚动查看某一条卡片时,从卡片进入详情页面后,返回时希望页面能够滚动回原来的卡片位置,而不是顶部或其他位置。应该怎么办呢?
1. 方法简述
1.1 整体流程
- 离开页面时:
- 记录当前滚动位置。
- 返回页面时:
- 判断是否需要刷新数据和还原滚动位置。
- 如果需要:
- 调用指定的刷新方法。
- 将滚动位置还原到离开时的值。
1.2 核心处理
import { computed, ref } from 'vue';
export default function useRefreshScroll({
fromList,
scrollDomRef,
queryDomRef = null,
queryFn = 'onRefresh',
}) {
// 存储离开时的位置
const preScrollTop = ref(0);
const scrollDom = computed(() => (scrollDomRef.value instanceof HTMLElement ? scrollDomRef.value : scrollDomRef.value?.$el));
// 页面进入前判断是否需要还原位置
const beforeEnter = async (to, from, next) => {
if (fromList.includes(from.name as string)) {
const resultQueryDomRef = queryDomRef ?? scrollDomRef;
const refreshFn = resultQueryDomRef.value?.[queryFn];
if (refreshFn) {
await refreshFn();
}
setTimeout(() => {
if (scrollDom.value) {
scrollDom.value.scrollTop = preScrollTop?.value;
}
}, 300); // 观感考虑设置为300
}
next();
};
// 记录离开时的位置 scrollTop
const beforeLeave = (to, from, next) => {
preScrollTop.value = scrollDom?.value?.scrollTop ?? 0;
next();
};
return {
beforeLeave,
beforeEnter,
};
}
1.3 具体使用方法
<template>
<div ref="scrollDomRef" class="scroll-container">
<!-- 这里是滚动内容 -->
</div>
</template>
<script>
import { ref } from 'vue';
import useRefreshScroll from './path/to/useRefreshScroll';
export default {
name: 'MyComponent',
setup() {
const scrollDomRef = ref(null); // 滚动容器的 ref
const { beforeLeave, beforeEnter } = useRefreshScroll({
// 假设从 'home' 或 'profile' 页面跳转过来时需要恢复滚动位置
fromList: ['home', 'profile'],
scrollDomRef,
queryFn: 'onRefresh',
});
// 这里可以处理路由守卫...
return {
scrollDomRef,
beforeLeave,
beforeEnter,
};
},
};
</script>
2. 方法介绍
作用:根据条件判断是否需要刷新页面数据,并将页面滚动位置恢复到离开时的锚点位置。
参数如下:
/**
* @param fromList:Array<string>,页面导航的前置页面 name 列表。如果从这些页面返回,则触发数据刷新和滚动位置恢复逻辑。
* @param scrollDomRef:Ref,表示滚动容器的引用,可能是一个 DOM 节点或 Vue 组件实例。
* @param queryDomRef:Ref,用于调用数据刷新方法的组件引用,默认值是 scrollDomRef。
* @param queryFn:string,需要调用的刷新数据的方法名称,默认是 'onRefresh'。
* @return {*}
*/
部分代码注释:
1、scrollDom:返回滚动容器的实际 DOM 元素。如果 scrollDomRef.value 是一个 DOM 元素,则直接返回,如果其是 Vue 组件实例,则尝试获取根 DOM 元素(使用 $el )。
const scrollDom = computed(() =>
(scrollDomRef.value instanceof HTMLElement ? scrollDomRef.value : scrollDomRef.value?.$el)
);
2、beforeEnter 和 beforeLeave Vue Router 的路由守卫
在 beforeEnter 中,首先检查页面是否从某个特定页面(由 fromList 定义)跳转过来。如果是的话,它会检查是否需要执行 onRefresh 等操作,并且在视图渲染后恢复滚动位置。
在 beforeLeave 中,记录了当前页面的滚动位置 scrollTop,以便在进入下一个页面时恢复这个位置。
这两个方法会在 Vue Router 的生命周期钩子 beforeRouteEnter、beforeRouteLeave 中使用。
3、next() 是 Vue Router 生命周期钩子中的回调函数,用来继续路由的生命周期。
eg:在 beforeEnter 和 beforeLeave 调用,通知 Vue Router 继续执行路由切换操作。
总结:
通过 Vue 的响应式 API 和导航守卫,实现了动态刷新与滚动位置恢复的功能,更好的提升用户体验。