先搞懂,Vue Router与当前路由是什么?
做Vue项目开发时,路由管理是绕不开的环节,而“知道当前用户在哪个路由页面”更是高频需求——比如导航栏要高亮当前页、根据路由参数发起接口请求、权限控制时判断路径…那在Vue Router里到底怎么获取当前路由信息呢?这篇文章从基础到场景,把常用方法和坑点一次性讲透~
Vue Router是Vue生态里管理单页面应用(SPA)路由的核心工具,它能让我们通过不同URL路径,渲染对应的组件、传递参数,而“当前路由”本质是个路由对象,里面存着当前页面的关键信息,常见的有这些:
path
:当前路由的路径(比如/user/123
)params
:动态路由参数(比如/user/:id
里的{ id: '123' }
)query
:URL上的查询参数(比如?name=张三
对应的{ name: '张三' }
)meta
:路由元信息(配置在路由规则里的自定义数据,比如权限标识requiresAuth: true
)
简单说,获取当前路由,就是拿到包含这些信息的对象~接下来看具体怎么拿!
选项式API里用 this.$route
如果你用的是Vue 2,或者Vue 3但写的是选项式组件(比如export default { data() {}, methods: {} }
这种写法),直接在组件里通过 this.$route
就能拿到当前路由对象。
举个实际开发的例子:假设要做“导航栏高亮”,在mounted
钩子或者methods
里判断路径:
<template> <nav> <a :class="{ active: isHome }">首页</a> <a :class="{ active: isUser }">用户页</a> </nav> </template> <script> export default { computed: { isHome() { return this.$route.path === '/'; // 对比当前路径是否是首页 }, isUser() { return this.$route.path.startsWith('/user'); // 匹配用户页路径 } } } </script>
这里要注意两个点:
this
的指向:必须在Vue组件的实例上下文里用(比如methods
、computed
、生命周期钩子),如果用箭头函数定义方法,this
会丢失,变成undefined
!- 路由对象是响应式的:当路由变化时(比如从
/user/1
跳到/user/2
),this.$route
会自动更新,所以基于它的计算属性、绑定的class也会自动响应变化~
组合式API里用 useRoute
如果你的项目是Vue 3 + Composition API(尤其是用<script setup>
语法糖),获取路由得用组合式API提供的useRoute
,步骤很简单:
- 从
vue-router
里导入useRoute
; - 在
setup
函数(或<script setup>
)里调用useRoute
,得到路由对象。
看例子:在用户详情页,根据路由参数id
发请求(用<script setup>
写法):
<template> <div v-if="userInfo">用户{{ userInfo.name }}的详情</div> </template> <script setup> import { onMounted, watch } from 'vue' import { useRoute } from 'vue-router' import { fetchUser } from '@/api/user' // 假设的接口请求函数 const route = useRoute() // 获取当前路由对象 const userInfo = ref(null) // 方式1:mounted时根据参数请求 onMounted(() => { const userId = route.params.id // 取动态路由参数id fetchUser(userId).then(res => { userInfo.value = res.data }) }) // 方式2:监听路由参数变化(比如从/user/1跳转到/user/2时自动重新请求) watch( () => route.params.id, // 监听params里的id变化 (newId) => { fetchUser(newId).then(res => { userInfo.value = res.data }) }, { immediate: true } // 组件加载时立即执行一次 ) </script>
这里的route
和选项式API里的this.$route
作用一样,也是响应式的,但要注意:不能在组件外(比如单独的js工具文件)直接用useRoute
,因为它依赖Vue的组件上下文,必须在组件的setup
或<script setup>
里调用~
全局场景用 router.currentRoute
如果你的代码不在组件里(比如写了个全局的工具函数,或者在路由守卫之外的地方需要路由信息),这时候得用VueRouter实例的currentRoute
属性。
回忆下VueRouter的初始化:我们通常会在router/index.js
里创建路由实例:
// router/index.js import { createRouter, createWebHistory } from 'vue-router' import routes from './routes' const router = createRouter({ history: createWebHistory(), routes }) export default router
这个router
实例上的currentRoute
是个ref对象(Vue 3的响应式语法),所以要拿到实际的路由对象,得取router.currentRoute.value
。
举个场景:在全局的权限拦截工具里,判断当前路径是否需要登录:
// utils/permission.js import router from '@/router' // 导入上面创建的router实例 export function checkAuth() { const currentRoute = router.currentRoute.value // 取当前路由对象 if (currentRoute.meta.requiresAuth && !isLogin()) { // 假设isLogin是判断登录态的函数 return false // 没登录且需要权限,拦截 } return true }
这里要注意异步问题:路由变化是异步的,如果在导航过程中(比如刚调用router.push
)立刻获取router.currentRoute.value
,可能拿到的是旧路由,这种情况需要等导航完成,或者用nextTick
处理(后面场景部分会详细讲)~
导航后获取“最新”路由
比如点击按钮跳转到新页面后,要立刻获取新路由的信息(比如统计埋点、修改页面标题),但router.push
是异步操作,直接在push
后拿路由可能拿到旧值:
// 错误示范:可能拿到旧路由 router.push('/user/123') console.log(this.$route.path) // 可能还是原来的路径!
解决方法有两种:
- 用
router.push
的回调:router.push
支持传回调,导航完成后执行:
router.push('/user/123', () => { console.log(this.$route.path) // 此时是新路径'/user/123' })
- 结合
nextTick
(Vue的异步更新队列):
import { nextTick } from 'vue' router.push('/user/123') await nextTick() // 等Vue更新完响应式数据(包括路由) console.log(this.$route.path) // 新路径
路由守卫里的路由信息
Vue Router的路由守卫(比如全局守卫beforeEach
、组件内守卫beforeRouteEnter
)里,本身就会传入to
(目标路由)和from
(当前路由)这两个参数,直接用就行~
举个全局守卫的例子,做权限控制:
// router/index.js router.beforeEach((to, from, next) => { if (to.meta.requiresAuth && !isLogin()) { // to是目标路由,判断是否需要权限 next('/login') // 没登录就跳登录页 } else { next() // 放行 } })
再看组件内守卫beforeRouteUpdate
(当路由参数变化,但组件复用的时候触发,比如/user/1
跳到/user/2
):
<script> export default { beforeRouteUpdate(to, from) { console.log('旧路由:', from.path) console.log('新路由:', to.path) this.fetchData(to.params.id) // 根据新参数重新请求数据 } } </script>
注意:beforeRouteEnter
里拿不到this
(因为组件还没创建),所以如果要在进入组件前用路由信息,得通过next
传回调:
<script> export default { beforeRouteEnter(to, from, next) { next(vm => { // vm是组件实例 vm.initData(to.params.id) // 用vm访问组件方法 }) } } </script>
监听路由变化做响应
有时候路由变化时,要自动执行逻辑(比如参数变化重新发请求、页面切换时销毁定时器),这时候可以监听路由对象的变化:
- 选项式API里,用
watch
监听$route
:
<script> export default { watch: { '$route.params.id'(newId, oldId) { // 监听params里的id变化 this.fetchData(newId) } } } </script>
- 组合式API里,用
watch
监听useRoute
拿到的route
:
<script setup> import { watch } from 'vue' import { useRoute } from 'vue-router' const route = useRoute() watch(route, (newRoute, oldRoute) => { console.log('路由从', oldRoute.path, '变到', newRoute.path) // 执行其他逻辑,比如根据newRoute.query.page重新请求列表 }, { deep: true }) // 如果要监听路由对象内部属性变化,开deep(不过一般路由对象是整体替换,所以也可以不开) </script>
问题1:为什么this.$route
是undefined
?
大概率是这两个原因:
- 组件没被Vue Router管理:比如这个组件不是通过
<router-view>
渲染的,而是直接写在App.vue
里的静态组件,这时组件上下文没有$route
; this
指向错误:比如在箭头函数里用this.$route
,导致this
不是组件实例,解决方法是把函数改成普通函数,或者用getCurrentInstance
获取组件实例(但尽量优先用选项式的写法避免this问题)。
问题2:路由变了,但页面数据没更新?
比如路由参数从/user/1
变到/user/2
,但组件里的数据还是用户1的,这是因为组件复用了(Vue Router默认策略:相同组件复用,不销毁重建),所以mounted
不会重新执行,解决方法:
- 用
beforeRouteUpdate
守卫,在路由变化时主动更新数据; - 用
watch
监听路由参数变化,触发数据请求; - 给
<router-view>
加key
,强制销毁重建组件(但性能略差,不推荐除非特殊情况)。
问题3:router.currentRoute.value
拿到的是旧路由?
因为router.push
等导航方法是异步的,路由更新需要时间,如果要在导航后立刻拿新路由,得等导航完成:
// 正确做法:等push完成后再拿 router.push('/user/123').then(() => { console.log(router.currentRoute.value.path) // 新路径 })
或者用await
配合nextTick
:
await router.push('/user/123') await nextTick() console.log(router.currentRoute.value.path)
问题4:解构路由属性后,响应式失效?
比如在组合式API里这样写:
<script setup> import { useRoute } from 'vue-router' const route = useRoute() const { path } = route // 这里解构后,path不是响应式的! watch(path, () => { // 永远监听不到变化 console.log('路径变了') }) </script>
原因是解构会破坏响应式,解决方法:直接用route.path
,或者监听整个route
对象:
// 正确写法:监听route或者route的属性函数 watch( () => route.path, (newPath) => { console.log('路径变了:', newPath) } )
选对方法,场景为王
获取当前路由的核心逻辑是拿到路由对象,但不同Vue版本(Vue 2/3)、不同代码位置(组件内/外)、不同场景(导航后/守卫里)对应的方法不同:
- 组件内(选项式)→
this.$route
- 组件内(组合式)→
useRoute
- 组件外(全局逻辑)→
router.currentRoute.value
同时要注意异步更新和响应式监听的细节,避免拿到旧数据或监听失效,实际开发中,先明确自己的代码环境(是选项式还是组合式?在组件内还是外?),再选对应的方法,遇到问题时对照上面的场景和坑点排查,基本就能搞定~
如果想更深入,建议去看Vue Router官方文档里的路由对象章节,里面对路由对象的属性、方法有更权威的解释~
版权声明
本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。