Vue Router 4结合页面过渡时滚动行为的实现方案
解决Vue Router 4过渡与滚动行为冲突的方案
问题原因
使用out-in模式的<transition>包裹顶层RouterView时,scrollBehavior会在旧页面退出阶段触发,此时新页面(首页)尚未挂载,导致无法找到目标滚动元素#menu。
实现步骤
1. 创建滚动辅助工具文件
新建scrollHelper.js,用于管理滚动状态和回调逻辑:
let pendingScrollTarget = null let resolveScrollPromise = null // 设置待滚动的目标元素选择器 export function setPendingScrollTarget(target) { pendingScrollTarget = target } // 过渡完成后触发滚动 export function triggerScroll() { if (pendingScrollTarget && resolveScrollPromise) { const targetEl = document.querySelector(pendingScrollTarget) // 找到元素则返回滚动配置,否则返回undefined跳过滚动 resolveScrollPromise(targetEl ? { el: pendingScrollTarget } : undefined) // 重置状态 pendingScrollTarget = null resolveScrollPromise = null } } // 获取延迟滚动的Promise export function getScrollPromise() { return new Promise(resolve => { resolveScrollPromise = resolve }) }
2. 修改App.vue中的过渡组件
在包裹RouterView的<transition>上监听after-enter事件,触发滚动逻辑:
<template> <router-view v-slot="{ Component }"> <transition mode="out-in" @after-enter="handleAfterEnter"> <component :is="Component" /> </transition> </router-view> </template> <script setup> import { triggerScroll } from './scrollHelper' const handleAfterEnter = () => { // 新页面过渡完成后执行滚动 triggerScroll() } </script>
3. 调整Router的scrollBehavior逻辑
将scrollBehavior改为异步函数,等待过渡完成后再执行滚动:
import { createRouter, createWebHashHistory } from 'vue-router' import { getScrollPromise, setPendingScrollTarget } from './scrollHelper' import routes from './routes' // 保留你原有的内容页判断逻辑 function isContentPage(route) { return route.meta?.isContentPage } const router = createRouter({ history: createWebHashHistory(), routes, async scrollBehavior(to, from) { if (to.name === 'index' && isContentPage(from)) { // 记录待滚动的目标元素 setPendingScrollTarget('#menu') // 等待过渡完成后返回滚动配置 return await getScrollPromise() } // 其他场景使用默认滚动行为 return undefined } }) export default router
原理说明
- 当需要滚动到首页
#menu时,先记录目标选择器并返回一个未完成的Promise,暂停Vue Router的滚动逻辑。 - 新页面过渡动画完成(
after-enter事件触发)后,再执行滚动操作,此时目标元素已挂载到DOM中,可被正确定位。
内容的提问来源于stack exchange,提问作者Igid
相关产品推荐
相关产品推荐

