Vue Hash 路由与锚点导航区别及Hash路由下锚点导航的解决方式
Hash 路由用#标识页面改变的是「你要看哪一页」锚点导航用#标识位置改变的是「你要滚到哪里」Hash 路由 https://example.com/#/about ↑ 这是路由路径决定加载哪个组件 锚点导航 https://example.com/page.html#section-2 ↑ 这是页面内位置决定滚动到哪里两者看起来都用了#但背后的机制完全不同Hash 路由锚点导航作用模拟多页面切换视图组件同一页面内定位到某个元素#后面是什么路由路径/about、/user/1元素 IDsection-1、top谁在管理Vue Router 接管浏览器原生行为页面是否刷新否只替换组件否只是滚动URL 变化方式router.push()/router-linklocation.hash/a[href]/scrollIntoView监听机制hashchange事件被 Vue Router 拦截hashchange事件浏览器默认处理各自的工作原理Hash 路由用户点击 router-link → 路由匹配 → 加载对应组件 → 渲染到 router-view URL: /#/home → /#/about → /#/user/42 ↓ ↓ ↓ Home组件 About组件 User组件(42)// Vue Router 内部做的事简化 window.addEventListener(hashchange, () { // 解析 hash 里的路径/#/about → /about // 匹配路由表 // 渲染对应组件 })锚点导航用户点击 a href#section-2 → 浏览器查找 idsection-2 的元素 → 滚动过去 页面 DOM: div idsection-1.../div div idsection-2.../div ← 滚到这里 div idsection-3.../div// 浏览器原生行为没有任何框架参与 // 点击 a href#section-2 时 // 1. URL 变成 当前页面#section-2 // 2. 滚动到 idsection-2 的元素hash路由与锚点导航冲突在 Vue Hash 路由的应用中浏览器默认的锚点行为被 Vue Router 打破了!-- 你的 Vue 应用 -- a href#section-1跳到第一节/a !-- 你以为会发生的事 -- !-- ✅ 滚动到 idsection-1 的元素 -- !-- 实际发生的事 -- !-- ❌ Vue Router 把 #section-1 当成路由去匹配 -- !-- ❌ 路由表里没有 /section-1 这条规则 -- !-- ❌ 可能跳到 404 或者首页页面不会滚动 --根本原因Vue Router 启动后会劫持所有hashchange事件#后面的任何内容都会被当成路由路径解析浏览器的原生锚点滚动行为就失效了。如何让两者共存// router/index.js const router createRouter({ history: createWebHashHistory(), routes: [...], scrollBehavior(to, from, savedPosition) { // Vue Router 把路由的 # 和锚点的 # 统一放到 to.hash 里 // to.path → 路由路径 // to.hash → 锚点部分 if (to.hash) { return { el: to.hash, behavior: smooth, top: 80, } } return { top: 0 } }, })此时的URL拆解 https://example.com/#/docs#install Vue Router 解析为 ┌─────────────────────────────┐ │ to.path /docs │ → 加载 Docs 组件 │ to.hash #install │ → 滚动到 #install └─────────────────────────────┘template !-- 跨页面 锚点先路由跳转再滚动 -- router-link to/docs#install安装指南/router-link !-- 同页面锚点不涉及路由切换 -- router-link to/#features功能介绍/router-link /templateHash 路由 → 管「去哪一页」→ 切组件锚点导航 → 管「去哪个位置」→ 滚页面两者共用 # → 冲突 → 用 scrollBehavior 解决具体解决方案如下方案一scrollBehavior同页面 跨页面通用这是 Vue Router官方推荐的处理方式// router/index.js import { createRouter, createWebHashHistory } from vue-router const router createRouter({ history: createWebHashHistory(), routes: [ { path: /, component: () import(/views/Home.vue) }, { path: /about, component: () import(/views/About.vue) }, { path: /docs, component: () import(/views/Docs.vue) }, ], scrollBehavior(to, from, savedPosition) { // ① 有锚点 → 滚动到目标元素 if (to.hash) { return new Promise((resolve) { // 等待页面异步组件加载完成 setTimeout(() { resolve({ el: to.hash, // CSS 选择器如 #section-1 behavior: smooth, top: 80, // 偏移固定导航栏高度 }) }, 300) // 给异步组件加载留时间 }) } // ② 浏览器前进/后退 → 恢复上次滚动位置 if (savedPosition) { return savedPosition } // ③ 默认滚到顶部 return { top: 0, behavior: smooth } }, }) export default router模板写法template nav !-- 同页面锚点 -- router-link to/#intro简介/router-link router-link to/#features功能/router-link !-- 跨页面锚点 -- router-link to/about#team关于我们 - 团队/router-link router-link to/docs#install文档 - 安装/router-link /nav /template方案二同页面编程式导航更精细的控制同一个页面内的锚点跳转不涉及路由切换template div classpage nav classsidebar a v-fornav in navList :keynav.id :class[nav-item, { active: current nav.id }] clickgoTo(nav.id) {{ nav.label }} /a /nav main classcontent section v-fornav in navList :keynav.id :idnav.id classsection h2{{ nav.label }}/h2 p{{ nav.content }}/p /section /main /div /template script setup import { ref, onMounted, onUnmounted } from vue const navList [ { id: intro, label: 简介, content: ... }, { id: features, label: 功能, content: ... }, { id: pricing, label: 定价, content: ... }, { id: faq, label: 常见问题, content: ... }, ] const current ref(intro) // ---- 点击跳转 ---- function goTo(id) { const el document.getElementById(id) if (!el) return const offset 80 // 固定头部高度 const top el.getBoundingClientRect().top window.scrollY - offset window.scrollTo({ top, behavior: smooth }) // 手动同步更新 URL 的 hash但不触发路由跳转 history.replaceState(null, , #${id}) } // ---- 滚动监听自动高亮 ---- let ticking false function onScroll() { if (ticking) return ticking true requestAnimationFrame(() { const offset 120 for (let i navList.length - 1; i 0; i--) { const el document.getElementById(navList[i].id) if (el el.getBoundingClientRect().top offset) { current.value navList[i].id break } } ticking false }) } onMounted(() window.addEventListener(scroll, onScroll, { passive: true })) onUnmounted(() window.removeEventListener(scroll, onScroll)) /script style scoped .nav-item.active { color: var(--accent); font-weight: 600; } .section { scroll-margin-top: 80px; /* CSS 原生偏移兜底 */ min-height: 60vh; padding: 2rem 0; } /style方案三自定义指令复用场景多个页面都需要锚点导航时封装成指令// directives/anchor.js export const vAnchor { mounted(el, binding) { el.style.cursor pointer el.addEventListener(click, (e) { e.preventDefault() const targetId binding.value const target document.getElementById(targetId) if (!target) return target.scrollIntoView({ behavior: smooth, block: start }) // 用 replaceState 不触发 popstate history.replaceState(null, , #${targetId}) }) }, }// main.js import { vAnchor } from ./directives/anchor app.directive(anchor, vAnchor)template nav a v-anchorintro简介/a a v-anchorfeatures功能/a a v-anchorpricing定价/a /nav /template常见坑和解法坑 1固定导航栏遮挡目标区域/* 最优雅的解法CSS scroll-margin-top */ [id] { scroll-margin-top: 80px; }// JS 兼容方案 function scrollToWithOffset(id, offset 80) { const el document.getElementById(id) if (!el) return const y el.getBoundingClientRect().top window.scrollY - offset window.scrollTo({ top: y, behavior: smooth }) }坑 2scrollBehavior中元素还没渲染异步组件或数据驱动的页面目标元素可能还没挂载scrollBehavior(to) { if (to.hash) { return new Promise((resolve) { // 轮询等待目标元素出现 const tryScroll (retries 10) { const el document.querySelector(to.hash) if (el) { resolve({ el: to.hash, behavior: smooth, top: 80 }) } else if (retries 0) { setTimeout(() tryScroll(retries - 1), 100) } else { resolve({ top: 0 }) // 兜底 } } tryScroll() }) } return { top: 0 } }坑 3滚动监听与 Vue Router 导航冲突// 在滚动监听中避免在路由跳转过程中误触发 import { useRouter } from vue-router const router useRouter() const isNavigating ref(false) router.beforeEach(() { isNavigating.value true }) router.afterEach(() { setTimeout(() { isNavigating.value false }, 500) }) function onScroll() { if (isNavigating.value) return // 路由跳转期间不处理 // ... 正常的高亮逻辑 }

相关新闻

Agent-S3终极指南:首个超越人类性能的智能体框架深度解析

Agent-S3终极指南:首个超越人类性能的智能体框架深度解析

Agent-S3终极指南:首个超越人类性能的智能体框架深度解析 【免费下载链接】Agent-S Agent S: an open agentic framework that uses computers like a human 项目地址: https://gitcode.com/GitHub_Trending/ag/Agent-S 在计算机自动化领域,传统脚…

2026/7/28 10:38:06 阅读更多 →
uniapp 实现 骨架屏组件- u-skeleton

uniapp 实现 骨架屏组件- u-skeleton

u-skeleton UniApp 骨架屏组件 概览 什么是骨架屏 为什么需要骨架屏 组件特性 三种模式 插槽模式 遮罩模式 行列模式 模式决策流程 子组件 skeleton-item skeleton-text skeleton-avatar skeleton-image 参考 容器 Props 子组件 Props 动画说明 常见问题 …

2026/7/28 10:38:06 阅读更多 →
connect 断线重连

connect 断线重连

本篇核心内容:实现带自动断线重连功能的 TCP 客户端。 断线重连是线上长连接程序高频刚需,大家日常打游戏时就能直观感受到:本地网络波动、服务器崩溃、网络完全中断,游戏客户端都会自动重试连接;达到最大重试次数后&…

2026/7/28 10:38:06 阅读更多 →

最新新闻

如何彻底解决显卡驱动冲突:Display Driver Uninstaller终极清理指南

如何彻底解决显卡驱动冲突:Display Driver Uninstaller终极清理指南

如何彻底解决显卡驱动冲突:Display Driver Uninstaller终极清理指南 【免费下载链接】display-drivers-uninstaller Display Driver Uninstaller (DDU) a driver removal utility / cleaner utility 项目地址: https://gitcode.com/gh_mirrors/di/display-drivers…

2026/7/28 10:46:08 阅读更多 →
SMT电子物料检验全流程与关键要点解析

SMT电子物料检验全流程与关键要点解析

1. SMT工厂电子物料检验的重要性在SMT(表面贴装技术)生产线上,电子物料的质量直接决定了最终产品的可靠性和良品率。我见过太多因为物料问题导致整批产品报废的案例,损失动辄数十万元。有一次,某批电阻来料存在氧化问题…

2026/7/28 10:46:08 阅读更多 →
【Python】策略模式-简介

【Python】策略模式-简介

策略模式(Strategy Pattern) 是 Python 设计模式中非常经典的一种行为型模式。它的核心思想是:定义一系列算法(或业务逻辑),把它们一个个封装起来,并使它们可以互相替换。简单来说,就…

2026/7/28 10:46:08 阅读更多 →
知网查重用什么降重工具?按知网适配排一排

知网查重用什么降重工具?按知网适配排一排

知网查重用什么降重工具?按知网适配排一排 你学校要求查知网,这句话其实已经把选降重工具的范围圈死了一大半。很多人吃亏就吃在这:随便找了个降重工具,宣传说降幅多猛多猛,结果自己降完拿去知网一查,重复…

2026/7/28 10:46:08 阅读更多 →
数论三题:排列数、亲和数与分拆素数和的算法实践

数论三题:排列数、亲和数与分拆素数和的算法实践

1. 数学趣题三合一:排列数、亲和数与分拆素数和 作为一名数学爱好者,我最近在整理几个经典的数论问题,发现"排列数亲和数分拆素数和"这三个看似不相关的概念,在实际应用中却有着微妙的联系。今天就来分享这些有趣的数学…

2026/7/28 10:46:08 阅读更多 →
一文读懂one-nio配置系统:如何优雅地管理你的应用参数

一文读懂one-nio配置系统:如何优雅地管理你的应用参数

一文读懂one-nio配置系统:如何优雅地管理你的应用参数 【免费下载链接】one-nio Unconventional I/O library for Java 项目地址: https://gitcode.com/gh_mirrors/on/one-nio one-nio是一个非常规的Java I/O库,其配置系统提供了简洁高效的参数管…

2026/7/28 10:45:08 阅读更多 →

日新闻

告别臃肿!3步让你的暗影精灵笔记本重获新生

告别臃肿!3步让你的暗影精灵笔记本重获新生

告别臃肿!3步让你的暗影精灵笔记本重获新生 【免费下载链接】OmenSuperHub Control Omen laptop performance, fan speeds, and keyboard lighting, and unlock power limits. 项目地址: https://gitcode.com/gh_mirrors/om/OmenSuperHub 你是否也曾为官方Om…

2026/7/28 0:00:43 阅读更多 →
RAG必踩坑!财报法规检索不准?这款开源工具让答案浮出水面,准确率飙升98.7%!

RAG必踩坑!财报法规检索不准?这款开源工具让答案浮出水面,准确率飙升98.7%!

做 RAG 的人应该都踩过这个致命的坑:把几百页的财报、法规、技术手册扔给向量库,问一个具体问题,搜出来的全是沾边但没用的内容 —— 关键信息要么被硬切块拆碎了,要么藏在几十条结果的最下面。语义相似≠真正相关,这个…

2026/7/28 0:00:43 阅读更多 →
抖音视频文案提取工具全指南:免费2026版、手机App、在线工具一网打尽

抖音视频文案提取工具全指南:免费2026版、手机App、在线工具一网打尽

2026年做短视频运营,从抖音上扒文案早就不是偷偷抄笔记的事了。我刚开始做内容的时候,每天刷半小时抖音,手动把爆款视频的口播敲进备忘录,一条2分钟的视频得花十来分钟,碰到语速快的还要反复回听。后来试了一圈工具&am…

2026/7/28 0:00:43 阅读更多 →

周新闻

深度学习道路桥梁裂缝检测系统 道路桥梁裂缝检测数据集 道路桥梁病害识别检测数据集

深度学习道路桥梁裂缝检测系统 道路桥梁裂缝检测数据集 道路桥梁病害识别检测数据集

深度学习道路桥梁裂缝检测系统 数据集6000张 完整源码已标注数据集训练好的模型环境配置教程程序运行说明文档,可以直接使用!系统支持图片、视频、摄像头等多种方式检测裂缝,功能强大实用。 1数据集6000张 8各类别

2026/7/27 4:33:59 阅读更多 →
深度学习YOLO模型如何训练 PUBG 绝地求生目标检测数据集

深度学习YOLO模型如何训练 PUBG 绝地求生目标检测数据集

pubg数据集 精选原图1.42万数据 1.49万标签 无任何重复、算法增强或冗余图像! pubg绝地求生目标检测数据集 1分类:e_body,14905个标签,txt格式 共计14244张图,99%为640*640尺寸图像 适合yolo目标检测、AI训练关键词&am…

2026/7/28 8:29:16 阅读更多 →
Apex英雄目标检测数据集 深度学习框架YOLO如何训练APEX数据集

Apex英雄目标检测数据集 深度学习框架YOLO如何训练APEX数据集

Apex检测数据集数据集详情检测类别: allies enemy tag图片总量:7247张训练集:5139张验证集:1425张测试集:683张标注状态:全部已标注,即拿即用数据格式:支持YOLO格式及其他格式&#…

2026/7/28 5:03:42 阅读更多 →

月新闻