Vue记住滚动条位置 scrollBehavior + debounce
Vue记住滚动条位置1. 先给占位符加一层缓存2. 在路由上加一个源信息meta,记录滚动条的top值3. 使用 scrollBehavior4. 给页面添加事件监听使用前端路由,当切换到新路由时,想要页面滚到顶部,或者是保持原先的滚动位置,就像重新加载页面那样。 vue-router 可以自定义路由切换时页面如何滚动。注意:这个功能只在支持history.pushState的浏览器中可用1. 先
使用前端路由,当切换到新路由时,想要页面滚到顶部,或者是保持原先的滚动位置,就像重新加载页面那样。
vue-router
可以自定义路由切换时页面如何滚动。
注意:这个功能只在支持
history.pushState的浏览器中可用
1. 先给占位符加一层缓存
Home
是需要记住滚动条位置的组件
<!-- 路由占位符 -->
<keep-alive include="Home">
<router-view></router-view>
</keep-alive>
2. 在路由上加一个源信息meta,记录滚动条的top值
const routes = [
{
path: '/home',
component: Home,
meta: {
isRecord: true,
top: 0
}
}
]
3. 使用 scrollBehavior
在文档页面(http://localhost:8080/document)拉动滚动条,然后刷新浏览器会发现滚动条依然在原来的位置,这是浏览器的默认行为,会记录浏览器滚动条默认位置。
但是点击浏览器“前进/后退”按钮,会发现当初那个页面的滚动条从0开始了,没有记录上一次滚动条的位置。现在要求点击浏览器“前进/后退”按钮,页面滚动条要记录上一次的位置,这时需要设置它的的滚动行为。
这时候需要在路由配置中设置 scrollBehavior(to,from,savePosition)函数,函数有三个参数。scrollBehavior() 函数在点击浏览器的“前进/后退”,或者切换导航的时候触发。
- to:要进入的目标路由对象,到哪里去
- from:离开的路由对象,哪里来
- savePosition:会记录滚动条的坐标,点击前进/后退的时候记录值{x:?,y:?}
const router = new VueRouter({
routes: [...],
scrollBehavior (to, from, savedPosition) {
// return 期望滚动到哪个的位置
}
})
第三个参数 savedPosition 当且仅当 popstate 导航 (通过浏览器的 前进/后退 按钮触发) 时才可用。
在该方法内,可以通过判断路由to,from两个对象来做一些必要的判断;
savedPosition 参数是记录的上次滚动的位置;
通过return {x:number,y:number}来控制页面滚动的位置;
- 对于所有路由导航,简单地让页面滚动到顶部。
scrollBehavior (to, from, savedPosition) {
return{x:0, y:0}
}
- 想要在后退时,滚动到上次滚动的位置,如果满足条件,savedPosition有值的情况下:
scrollBehavior (to, from, savedPosition) {
if (savedPosition) {
return savedPosition
} else {
return { x: 0, y: 0 }
}
}
-
新增情况:异步滚动
项目中经常会遇到异步请求接口数据,这些异步操作通常会放在created、mounted钩子里面
分析:通过在不同的生命周期钩子和scrollBehavior()使用console.log()打印顺序如下:-
created钩子
-
mounted钩子
-
to参数: {} from参数: {}
-
异步请求数据:get data
-
打印位置信息 savedPosition {x: 0, y: 630}
可知:必须使用异步滚动,否则,请求的数据重新赋值,将导致页面重新渲染
-
当页面数据需要请求加载有延迟的情况下,页面如果直接滚动,会出现滚动后,页面数据请求回来,DOM重新渲染,滚动失效的情况;
必须使用异步滚动,利用setTimeout跳出主线程将回调事件放到队列中。由于mouted比scrollBehavior函数早执行,所以异步请求的回调事件优先进入队列,接下去才是setTimeout的回调事件。根据队列 先进先出的原理。先执行了异步请求回调事件对data中的变量a做赋值操作。此时相当于这已经是个静态页面了,接着我只要执行return { x:0, y: 100 }。这样就已经触发了页面滚动到100px的效果。但是由于data数据发生改变,页面重新渲染又回到顶部。这时整个轻触滚动效果已经暗中执行完成,不会再出现遮罩层了。
官方文档给补充了异步滚动的方法:
scrollBehavior (to, from, savedPosition) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({ x: 0, y: 0 })
}, 500)
})
}
这个会在返回后,有一定延迟再滚动,可以根据自己项目的具体情况进行一定修改,兼容;
-
最终结果
// 创建路由对象 const router = new VueRouter({ routes, scrollBehavior(to, from, savedPosition) { console.log(to.meta.top) // if (savedPosition) { // return savedPosition // } else { // return { x: 0, y: to.meta.top || 0 } // } return new Promise((resolve, reject) => { setTimeout(() => { if (savedPosition) { resolve(savedPosition) } else { resolve({ x: 0, y: to.meta.top || 0 }) } }, 100) }) } })
4. 给页面添加事件监听
Home.vue
// 当组件第一次被创建的时候 先触发 created 再触发 activated
// 之后被激活的时候 触发 activated
activated() {
fn = this.recordTopHandler()
// 添加滚动的事件监听
window.addEventListener('scroll', fn)
},
// 当组件被缓存的时候 触发 deactivated
deactivated() {
// 移除事件监听
window.removeEventListener('scroll', fn)
}
fn 函数
用来记录滚动条的位置
- 使用了一个防抖函数
_.debounce(func, [wait=0], [options=])
,官方文档:lodash.debounce - _.debounce方法的作用是防抖动,当你的事件在不断触发的时候,会根据你设置的间隔时间只触发一次回调。也就是说当调用动作n毫秒后,才会执行该动作,若在这n毫秒内又调用此动作则将重新计算执行时间。(空闲时间大于,设定的时间是才会执行!!!)
recordTopHandler() {
return _.debounce(
() => {
this.$route.meta.top = window.scrollY
},
50,
{ trailing: true }
)
}
更多推荐
所有评论(0)