前言
无论是单页面应用(SPA)还是复杂的后台管理系统,路由(Router)都是其灵魂。它通过 URL 映射组件,实现了无刷新的页面切换。本文将从底层原生 API 出发,带你彻底弄懂 Vue Router 的运行机制。
一、 路由的本质:Hash vs History
前端路由的核心是:改变 URL,页面不刷新,但渲染不同的组件。 Vue Router 本质上是基于浏览器原生的 window.location.hash 和 history API 实现的,通过监听 URL 变化,动态匹配路由规则并渲染对应组件,无需后端参与页面切换。
1. Hash 模式 (window.location.hash)
- URL 特征 :路径中携带
#符号,例如http://xxx.com/#/home、http://xxx.com/#/about - 底层依赖 :
window.location.hash - 核心特性 :URL 中
#后的内容属于锚点定位,不会发送到服务器端 ,所有前端路由请求最终都会指向域名/index.html,服务器只需返回首页文件即可。 - 优势:无需额外配置服务器,刷新页面、直接访问子路由都不会出现 404 错误,兼容性极强。
2. History 模式 (window.history)
- URL 特征 :路径中无
#符号,形态更简洁,例如http://xxx.com/home、http://xxx.com/about - 底层依赖 :浏览器原生
historyAPI - 核心坑点 :当用户刷新页面、直接访问子路由时,浏览器会向服务器发送对应路径的 GET 请求(如请求
/home),如果服务器未配置路由指向,会直接返回 404 错误。 - 解决方案 :必须在 Nginx 等服务器中配置规则,将所有路由请求都指向项目入口
index.html,由前端路由接管匹配逻辑。
Nginx
location / {
root /usr/share/nginx/html;
index index.html index.htm;
# 关键:找不到资源时返回 index.html
try_files $uri $uri/ /index.html;
}
二、 底层原理实现
1. Hash 模式实现链路
- 监听变化 :基于
window的hashchange事件,监听 URL 中 hash 值的变化。 - 设置值 :修改
location.hash手动修改路由路径。 - 跳转 :使用
location.assign()实现路由跳转。 - 获取当前路径 :通过
location.href或location.hash解析。
2. History 模式实现链路
-
监听变化 :基于浏览器原生
popstate事件,仅监听浏览器前进/后退操作触发的路由变化。⚠️ 避坑点 :调用
history.pushState或replaceState改变 URL 时,并不会触发popstate。Vue Router 内部通过劫持这些方法手动触发了更新。 -
操作记录:
pushState(stateObj, title, url):添加历史记录。replaceState(stateObj, title, url):替换当前记录。
-
获取路径 :基于
window.location.pathname获取纯路径部分。 -
状态存储 :通过
history.state获取传给pushState的自定义对象。
三、 Vue 路由跳转实战
方法一:声明式导航 <router-link>
这是日常开发中最常用的方式,本质是对 <a> 标签的封装,默认无刷新跳转,语法简洁且支持路由参数传递。核心参数如下:
-
to(必传) :目标路由路径,支持字符串格式和对象格式
- 字符串格式:
<router-link to="/home">首页</router-link> - 对象格式:可搭配 name、query、params 实现精细化跳转
- 字符串格式:
-
name :通过路由名称跳转(推荐,避免路径硬编码),示例:
:to="{ name: 'About' }" -
query :传递查询参数,参数会拼接在 URL 中(刷新不丢失),示例:
:to="{ name: 'About', query: { name: 'test' } }",最终 URL:/about?name=test -
params :传递动态路由参数,参数不会拼接在 URL(刷新会丢失),必须配合 name 使用 ,示例:
:to="{ name: 'About', params: { id: 123 } }"注意:若路由规则中未定义动态参数(如
:id),仅通过 name + params 传参,刷新页面后 params 会丢失;解决办法:在路由规则中添加
:id(必传)或:id?(可选),例如path: '/about/:id?'
方法二:编程式导航 useRouter
通过 useRouter 获取路由实例,用代码控制路由跳转,适合非点击触发的场景(如接口请求成功后跳转、条件判断跳转、定时器跳转等)
vue
<script setup>
import { useRouter } from 'vue-router'
// 获取路由实例
const router = useRouter()
// 编程式跳转
const goToPage = () => {
// 1. push 跳转(新增历史记录,可返回)
router.push('/home')
// 对象格式跳转
router.push({ name: 'About', query: { name: 'test' } })
// 2. replace 跳转(替换历史记录,不可返回)
router.replace('/about')
// 3. 路由前进/后退
router.go(-1) // 后退一页
router.back() // 后退一页(等价 go(-1))
router.forward() // 前进一页(等价 go(1))
}
</script>
四、 Vue 路由监听三大方法
Vue 监听路由变化,本质是监听 route 对象(包含 path/params/query 等属性)的变化,触发自定义回调函数,常用于路由切换时更新数据、重置状态等场景.
1. 使用 watch + useRoute
通过 useRoute 获取当前路由对象,搭配 watch 监听器实现路由变化监听,支持立即执行、深度监听,适用性最广。
vue
const route = useRoute();
watch(
() => route.query,
(newQuery) => {
console.log('搜索参数变了:', newQuery);
},
{ immediate: true, deep: true } // immediate 确保初始化时执行
);
2. 路由守卫 onBeforeRouteUpdate
Vue Router 提供的导航守卫,仅在组件复用时触发 (例如 /detail/123 → /detail/456),路由跳转到其他组件时不会触发,适合列表页跳转详情页等场景。
- 优点 :不需要
watch那么大的开销,专门针对参数更新。 - 局限:离开该组件或首次进入时不触发。
vue
<script setup>
import { onBeforeRouteUpdate } from 'vue-router'
// 组件复用时触发
onBeforeRouteUpdate((to, from) => {
console.log('即将跳转至:', to.path)
console.log('从:', from.path, '跳转而来')
// 可在此处更新组件数据
})
</script>
3. 原生监听(底层方案)
直接监听浏览器原生路由事件,脱离 Vue Router API 实现监听,适合特殊定制场景,需注意事件解绑避免内存泄漏。
window.addEventListener('popstate', callback)。