vue-router页面内手动滚动时router-link未自动添加active-class的解决办法
问题根因
Vue Router 仅会在路由显式变更时触发链接激活类的匹配逻辑,手动滚动页面不会自动更新当前路由的hash值,因此对应视口区块的router-link无法自动添加exact-active-class。
修复方案
可根据项目场景二选一:
方案1:监听滚动位置同步路由hash(保持原有router-link激活逻辑不变)
第一步:配置路由scrollBehavior,保证点击跳转逻辑正常
在路由初始化配置中添加滚动行为规则:
const router = createRouter({ history: createWebHistory(), routes: [], // 业务路由配置 scrollBehavior(to) { if (to.hash) { return { el: to.hash, behavior: 'smooth' } } return { top: 0 } } })
第二步:注册区块可见性监听,自动同步当前hash
使用IntersectionObserver监听锚点区块的可见状态,当区块进入视口指定区域时,替换当前路由的hash值,触发router-link的自动激活类匹配。推荐把逻辑封装为可复用的组合式函数:
import { onMounted, onUnmounted } from 'vue' import { useRouter } from 'vue-router' // 入参为所有锚点区块的id数组 export function useAnchorActive(sectionIds) { const router = useRouter() let observer = null onMounted(() => { observer = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { // 替换hash,不新增浏览历史、不触发强制跳转 router.replace({ hash: `#${entry.target.id}`, force: true }) } }) }, { // 视口上下各裁剪50%,仅当区块进入视口中间位置时判定为激活 rootMargin: '-50% 0px -50% 0px', threshold: 0 }) sectionIds.forEach(id => { const section = document.getElementById(id) if (section) observer.observe(section) }) }) onUnmounted(() => { observer?.disconnect() }) }
在导航栏组件中直接引入调用即可:
useAnchorActive(['features', 'pricing', 'about', 'contact'])
方案2:自行维护激活状态,手动绑定类名(更灵活,无路由依赖)
如果不想改动路由逻辑,可以直接监听滚动事件,自行计算当前处于视口的区块,给对应链接手动绑定激活类,示例代码如下:
<template> <nav class="landing-nav"> <router-link v-for="nav in navList" :key="nav.target" :to="nav.target" target="_self" class="nav-item" :class="{ 'exact-active-class': activeTarget === nav.target }" > {{ nav.label }} </router-link> </nav> </template> <script setup> import { ref, onMounted, onUnmounted } from 'vue' const navList = [ { label: 'features', target: '#features' }, { label: 'pricing', target: '#pricing' }, { label: 'about', target: '#about' } ] const activeTarget = ref(navList[0].target) let scrollListener = null onMounted(() => { scrollListener = () => { // 偏移量适配固定导航栏高度,比如导航栏高80px就设为80 const offsetTop = 80 for (const nav of navList) { const section = document.querySelector(nav.target) if (!section) continue const rect = section.getBoundingClientRect() if (rect.top <= offsetTop && rect.bottom >= offsetTop) { activeTarget.value = nav.target break } } } // 滚动时监听,passive标记优化滚动性能 window.addEventListener('scroll', scrollListener, { passive: true }) // 页面初始化时执行一次,匹配首屏状态 scrollListener() }) onUnmounted(() => { window.removeEventListener('scroll', scrollListener) }) </script>
注意:如果页面存在固定在顶部的导航栏,请根据导航栏实际高度调整
rootMargin或者offsetTop的数值,避免激活状态和实际视口内容错位。
内容的提问来源于stack exchange,提问作者morteza mortezaie
相关产品推荐
相关产品推荐

