Vue Router scrollBehavior
:让你的页面滚动起来!
大家好,我是你们今天的滚动行为大师(自封的),今天咱们来聊聊 Vue Router 里面那个神秘又实用的 scrollBehavior
选项。 别怕,虽然源码听起来吓人,但咱们的目标是把它扒得精光,让你以后也能自信地控制页面的滚动行为,让用户体验更上一层楼!
什么是 scrollBehavior
?
简单来说,scrollBehavior
就是 Vue Router 提供的一个钩子函数,允许你在路由切换时自定义页面的滚动位置。 想象一下,你在一个长长的页面上,点击链接跳转到另一个页面,如果没有 scrollBehavior
,页面可能会保持原来的滚动位置,这在某些情况下会很糟糕。 比如,你从页面底部跳到另一个页面,结果新页面也停留在底部,用户还得自己往上滚,用户体验直接打骨折!
scrollBehavior
就像一个贴心的管家,帮你记住或者调整滚动位置,让页面跳转更加自然流畅。
scrollBehavior
的基本用法
先来个最简单的例子,看看 scrollBehavior
怎么用:
const router = new VueRouter({
routes: [...], // 你的路由配置
scrollBehavior (to, from, savedPosition) {
// 始终滚动到页面顶部
return { x: 0, y: 0 }
}
})
这段代码的意思是,无论你从哪个页面跳转到哪个页面,都会滚动到新页面的顶部。 scrollBehavior
函数接收三个参数:
to
: 即将要进入的目标路由对象。from
: 当前导航正要离开的路由对象。savedPosition
: 只有在使用popstate
导航 (例如点击浏览器的后退/前进按钮) 时才可用。它记录了上一次页面的滚动位置。
scrollBehavior
函数的返回值是一个对象,包含 x
和 y
属性,分别表示水平和垂直方向的滚动位置。 如果返回 false
或 null
,则不会发生滚动。
scrollBehavior
的高级用法
scrollBehavior
可不仅仅是滚动到顶部这么简单,它还能做很多事情,比如:
-
恢复滚动位置: 使用
savedPosition
参数可以恢复用户之前的滚动位置,这在返回按钮时非常有用。scrollBehavior (to, from, savedPosition) { if (savedPosition) { return savedPosition } else { return { x: 0, y: 0 } } }
-
滚动到指定元素: 可以使用
to.hash
属性来获取 URL 中的锚点,然后滚动到对应的元素。scrollBehavior (to, from, savedPosition) { if (to.hash) { return { selector: to.hash, behavior: 'smooth', // 添加平滑滚动效果 offset: {x: 0, y: 100} // 滚动到元素上方100px的位置 } } else if (savedPosition) { return savedPosition } else { return { x: 0, y: 0 } } }
注意
selector
属性,它的值是一个 CSS 选择器,用于选中要滚动的元素。 还可以使用behavior: 'smooth'
添加平滑滚动效果,让滚动更加自然。offset
属性可以指定滚动到元素上方的偏移量,可以避免被固定头部遮挡。 -
自定义滚动逻辑: 你可以根据
to
和from
对象中的信息,编写更复杂的滚动逻辑。scrollBehavior (to, from, savedPosition) { if (to.meta.scrollToTop) { return { x: 0, y: 0 } } else if (savedPosition) { return savedPosition } else { return { x: 0, y: 0 } } }
在这个例子中,我们使用
to.meta.scrollToTop
来判断是否需要滚动到顶部。 你可以在路由配置中为每个路由添加meta
属性,用于存储自定义的信息。
scrollBehavior
源码分析
接下来,我们深入 Vue Router 的源码,看看 scrollBehavior
是如何实现的。 (放心,不会让你一行一行地啃代码,我会挑重点讲。)
Vue Router 的核心逻辑位于 src/
目录下。 scrollBehavior
的实现主要涉及到以下几个文件:
src/util/scroll.js
: 包含滚动相关的工具函数。src/history/base.js
: 定义了路由历史记录的基类,包含了scrollBehavior
的调用逻辑。
我们主要关注 src/history/base.js
文件中的 updateRoute
方法,这个方法负责更新路由,并在更新完成后调用 scrollBehavior
。
下面是 updateRoute
方法中与 scrollBehavior
相关的代码片段(简化版):
updateRoute (route: Route) {
const prev = this.current
this.current = route
this.cb && this.cb(route) // 通知监听器路由已更新
this.transitionTo(route, (route) => {
//... 省略了一些代码
if (this.options.scrollBehavior) {
this.handleScroll(route, prev, isPop)
}
})
}
可以看到,在路由更新完成后,会判断是否存在 scrollBehavior
选项,如果存在,则调用 handleScroll
方法。
handleScroll
方法的代码如下:
handleScroll (to: Route, from: Route, isPop: boolean) {
const router = this.router
if (!router.app) {
return
}
const behavior = this.options.scrollBehavior
if (!behavior) {
return
}
if (isPop && this.savedPosition) {
this.savedPosition = null
}
const savedPosition = isPop ? this.savedPosition : null
// 获取滚动位置
let position
try {
position = behavior.call(router, to, from, savedPosition)
} catch (e) {
return
}
// 处理滚动位置
this.handleScrollPosition(position, to)
}
这个方法做了以下几件事:
- 判断
scrollBehavior
是否存在: 如果不存在,则直接返回。 - 获取
savedPosition
: 如果是popstate
导航,则从this.savedPosition
中获取保存的滚动位置。 - 调用
scrollBehavior
: 调用scrollBehavior
函数,并将to
、from
和savedPosition
作为参数传递给它。 - 处理滚动位置: 调用
handleScrollPosition
方法处理scrollBehavior
函数返回的滚动位置。
handleScrollPosition
方法的代码如下:
handleScrollPosition (position: any, to: Route) {
if (!position) return false
// 如果是 selector 类型,则滚动到指定元素
if (typeof position === 'object') {
if (typeof position.selector === 'string') {
// 等待 DOM 更新后再滚动
this.app.$nextTick(() => {
const el = document.querySelector(position.selector)
if (el) {
// 获取元素在页面中的位置
const rect = el.getBoundingClientRect()
// 滚动到元素的位置
window.scrollTo(rect.left + window.pageXOffset, rect.top + window.pageYOffset);
}
})
} else if (typeof position.x === 'number' || typeof position.y === 'number') {
// 滚动到指定位置
window.scrollTo(position.x, position.y)
}
}
}
这个方法根据 position
的类型进行不同的处理:
- 如果
position
是一个包含selector
属性的对象: 则使用document.querySelector
选中对应的元素,并滚动到该元素的位置。 - 如果
position
是一个包含x
和y
属性的对象: 则直接使用window.scrollTo
方法滚动到指定的位置。
scrollBehavior
源码总结
总的来说,scrollBehavior
的实现并不复杂,它主要做了以下几件事:
- 在路由更新完成后,调用
scrollBehavior
函数。 - 将
to
、from
和savedPosition
作为参数传递给scrollBehavior
函数。 - 根据
scrollBehavior
函数的返回值,滚动到指定的位置或元素。
通过分析源码,我们可以更深入地理解 scrollBehavior
的工作原理,从而更好地使用它来控制页面的滚动行为。
scrollBehavior
使用技巧
-
利用
meta
属性: 可以在路由配置中使用meta
属性来存储自定义的信息,然后在scrollBehavior
函数中根据这些信息来控制滚动行为。// 路由配置 { path: '/page1', component: Page1, meta: { scrollToTop: true } }, { path: '/page2', component: Page2, meta: { scrollToTop: false } } // scrollBehavior 函数 scrollBehavior (to, from, savedPosition) { if (to.meta.scrollToTop) { return { x: 0, y: 0 } } else if (savedPosition) { return savedPosition } else { return { x: 0, y: 0 } } }
-
使用
nextTick
: 在滚动到指定元素时,可以使用this.$nextTick
来确保 DOM 已经更新完成。scrollBehavior (to, from, savedPosition) { if (to.hash) { return new Promise((resolve, reject) => { setTimeout(() => { resolve({ selector: to.hash, behavior: 'smooth', offset: {x: 0, y: 100} }) }, 500) }) } else if (savedPosition) { return savedPosition } else { return { x: 0, y: 0 } } }
-
异步滚动: 由于 Vue Router 的
scrollBehavior
是同步执行的, 如果页面组件在路由跳转后需要加载数据才能确定最终的滚动位置,可以使用 Promise 来延迟滚动操作。scrollBehavior (to, from, savedPosition) { if (to.hash) { return new Promise((resolve, reject) => { // 模拟异步加载数据 setTimeout(() => { resolve({ selector: to.hash, behavior: 'smooth', offset: {x: 0, y: 100} }) }, 500) }) } else if (savedPosition) { return savedPosition } else { return { x: 0, y: 0 } } }
常见问题和解决方案
问题 | 解决方案 |
---|---|
滚动位置不正确 | 1. 检查 selector 是否正确,确保能够选中要滚动的元素。 2. 使用 console.log 打印 getBoundingClientRect() 的返回值,查看元素的位置是否正确。 3. 使用 offset 属性调整滚动位置,避免被固定头部遮挡。 |
滚动没有平滑效果 | 确保 behavior 属性设置为 'smooth' 。 |
savedPosition 没有生效 |
1. 确保使用的是 popstate 导航 (例如点击浏览器的后退/前进按钮)。 2. 检查浏览器是否支持 history.scrollRestoration API,如果不支持,则需要手动实现滚动位置的保存和恢复。 |
滚动到元素上方被固定头部遮挡 | 使用 offset 属性调整滚动位置,例如 offset: { x: 0, y: -100 } ,表示滚动到元素上方 100px 的位置。 |
在Keep-Alive 组件中使用scrollBehavior |
在 Keep-Alive 组件中,组件会被缓存,导致每次进入组件时不会重新渲染,scrollBehavior 可能不会生效。 可以通过监听组件的 activated 和 deactivated 钩子函数,手动调用 scrollBehavior 函数来解决这个问题。 具体做法是: 在 activated 钩子函数中,获取当前路由的 to 和 from 对象,然后调用 scrollBehavior 函数,手动设置滚动位置。 在 deactivated 钩子函数中,保存当前的滚动位置,以便在下次进入组件时恢复滚动位置。 |
总结
scrollBehavior
是 Vue Router 中一个非常实用的选项,它可以帮助你自定义页面的滚动行为,提升用户体验。 通过学习本文,你应该已经掌握了 scrollBehavior
的基本用法、高级用法和源码实现。 希望你能灵活运用 scrollBehavior
,让你的页面滚动起来!
好了,今天的讲座就到这里,下次有机会再和大家分享其他 Vue Router 的技巧。 谢谢大家!