Skip to content

滚动行为

使用前端路由,当切换到新路由时,想要页面滚到顶部,或者是保持原先的滚动位置,就像重新加载页面那样。 vue-router 能做到,而且更好,它让你可以自定义路由切换时页面如何滚动

注意: 这个功能只在支持 history.pushState 的浏览器中可用。

js
const router = createRouter({
  history: createWebHashHistory(),
  routes: [...],
  scrollBehavior (to, from, savedPosition) {
    // return 期望滚动到哪个的位置
  }
})

scrollBehavior 函数接收 tofrom 路由对象,如 Navigation Guards。第三个参数avedPosition,只有当这是一个 popstate 导航时才可用(由浏览器的后退/前进按钮触发)

该函数返回一个ScrollToOptions位置对象

js
{
  top:.10,
  left:20,
  behavior:"smooth" ||"instant"||"auto"
}
js
const router = createRouter({
  scrollBehavior(to, from, savedPosition) {
    // 始终滚动到顶部
    return { top: 0 }
  },
})

也可以通过el传递一个 CSS 选择器或一个 DOM 元素 , 这种情况下,top/left会被视为对该元素的偏移量

js
const router = createRouter({
  scrollBehavior(to, from, savedPosition) {
    // 始终在元素 #main 上方滚动 10px
    return {
      // 也可以这么写
      // el: document.getElementById('main'),
      el: '#main',
      // 在元素上 10 像素
      top: 10,
    }
  },
})

如果返回false或一个空对象,name不会发生滚动

模拟滚动到锚点的行为

js
const router = createRouter({
  scrollBehavior(to, from, savedPosition) {
    if (to.hash) {
      return {
        el: to.hash,
      }
    }
  },
})

延迟滚动

有时候,我们需要在页面中滚动之前稍作等待。例如,当处理过渡时,我们希望等待过渡结束后再滚动。要做到这一点,你可以返回一个 Promise,它可以返回所需的位置描述符。下面是一个例子,我们在滚动前等待 500ms:

js
const router = createRouter({
  scrollBehavior(to, from, savedPosition) {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve({ left: 0, top: 0 })
      }, 500)
    })
  },
})

我们可以将其与页面级过渡组件的事件挂钩,以使滚动行为与你的页面过渡很好地结合起来

高级偏移量

如果你的页面中有固定的导航栏或类似的元素,你可能需要设置偏移量,以确保目标元素不会被其他内容遮挡。 使用静态偏移值并不总是有效。你可以尝试一些基于 CSS 的解决方案,比如使用 scroll-margin 或 scroll-padding 添加偏移,或者使用 ::before 和 ::after 伪元素。然而,这些方法有时会导致意想不到的行为。

在这种情况下,更好的做法是手动计算偏移量。一种简单的方法是结合 CSS 和 JavaScript 的 getComputedStyle()。这样每个元素都可以动态定义自己的偏移量。以下是一个示例

js
const router = createRouter({
  scrollBehavior(to, from, savedPosition) {
    const mainElement = document.querySelector('#main')
    if (mainElement) {
      const marginTop = parseFloat(getComputedStyle(mainElement).scrollMarginTop)
      return {
        el: mainElement,
        top: marginTop,
      }
    } else {
      return { top: 0 }
    }
  },
})