vue-router 怎么获取当前路由?
在Vue项目里,路由管理基本离不开vue-router,不管是做权限控制、导航高亮,还是埋点统计,经常需要知道“当前用户在哪个页面”——也就是获取当前路由信息,那vue-router到底怎么获取当前路由?不同场景下有啥不一样的方法?今天把常见情况拆开来聊聊。
先搞懂“当前路由”是个啥对象
vue-router里的“当前路由”,对应一个路由对象(Route Object),这个对象里存着当前页面路由的核心信息,
path
:当前页面的路径(比如/home
);name
:路由配置时定义的名称(如果配了name: 'Home'
,这里就是'Home'
);params
:动态路由参数(比如/user/:id
对应的id
值);query
:URL上的查询参数(比如?keyword=vue
对应的{ keyword: 'vue' }
);meta
:路由元信息(自定义的配置,比如meta: { requireAuth: true }
用来做权限标记)。
简单说,这个对象就是当前页面路由的“身份证”,里面的信息能帮我们判断页面状态、做逻辑分支。
组件内部怎么拿当前路由?
大部分业务逻辑写在组件里,所以组件内获取当前路由是最常见的场景,Vue2和Vue3的写法有差异,分开说:
Vue2(选项式API):用this.$route
在Vue2的组件里(比如.vue
文件的methods
、created
这些钩子中),直接通过this.$route
就能拿到当前路由对象,注意和this.$router
区分开:$router
是路由实例(用来做push
、replace
跳转),$route
才是当前路由对象。
举个例子:做一个需要判断页面的组件——如果当前在“关于我们”页面,就显示特殊文案:
<template> <div> <p v-if="isAboutPage">这是关于我们的专属内容</p> </div> </template> <script> export default { name: 'MyComponent', computed: { isAboutPage() { // 判断当前路由的path是否是/about return this.$route.path === '/about' } } } </script>
再比如,从列表页跳转到详情页时,需要用params
传ID,在详情页组件的created
钩子拿参数:
<script> export default { created() { const productId = this.$route.params.id this.fetchProductDetail(productId) // 调用接口拿详情数据 }, methods: { fetchProductDetail(id) { /* ... */ } } } </script>
Vue3(组合式API):用useRoute()
Vue3的组合式API更强调“按需导入”,所以要从vue-router
里导入useRoute
函数,在setup
语法糖(或setup()
函数)里用。
举个搜索页的例子:URL里有?keyword=xxx
,页面加载时要把搜索关键词回显到输入框:
<template> <input v-model="searchKeyword" placeholder="请输入关键词" /> </template> <script setup> import { useRoute, watch } from 'vue-router' import { ref, computed } from 'vue' const route = useRoute() // 拿到当前路由对象 // 初始化时从query里拿keyword const searchKeyword = ref(route.query.keyword || '') // 监听路由query变化(比如从其他页面带参数跳过来) watch( () => route.query, (newQuery) => { searchKeyword.value = newQuery.keyword || '' }, { immediate: true } ) </script>
这里要注意:useRoute()
必须在 setup 上下文里调用(比如<script setup>
或setup()
函数内),如果在普通函数里直接调用会报错,因为依赖Vue的响应式上下文。
路由守卫里怎么获取当前路由?
路由守卫是控制页面跳转权限、做导航拦截的关键,不管是全局守卫、路由独享守卫,还是组件内守卫,都能拿到“目标路由”(也就是即将进入的当前路由)和“来源路由”。
全局守卫:beforeEach
、beforeResolve
全局守卫作用于所有路由跳转,参数里的to
就是即将进入的目标路由(相当于“当前要去的路由”),from
是“当前离开的路由”。
比如做全局权限控制:某些页面需要登录后才能进,在main.js
里写:
import { createRouter, createWebHistory } from 'vue-router' import store from './store' // 假设用Vuex/Pinia存用户登录状态 const router = createRouter({ history: createWebHistory(), routes: [/* 路由配置 */] }) router.beforeEach((to, from, next) => { // 检查目标路由是否需要登录(通过meta字段标记) if (to.meta.requireAuth) { const isLogin = store.state.user.isLogin // 假设存了登录状态 if (isLogin) { next() // 已登录,放行 } else { next('/login') // 没登录,跳登录页 } } else { next() // 不需要登录,直接放行 } })
这里to
就是即将进入的路由对象,可以访问to.path
、to.meta
这些属性做判断。
路由独享守卫:beforeEnter
在单个路由的配置里,可以写beforeEnter
守卫,参数同样是to
、from
、next
,比如某个路由需要动态参数校验:
const routes = [ { path: '/order/:orderId', name: 'OrderDetail', component: OrderDetail, beforeEnter: (to, from, next) => { // 检查orderId是否是合法格式(比如数字) const orderId = to.params.orderId if (/^\d+$/.test(orderId)) { next() } else { next('/404') // 参数不合法,跳404 } } } ]
这里to.params.orderId
就是当前要进入的订单详情页的参数,通过beforeEnter
提前拦截非法参数。
组件内守卫:beforeRouteEnter
、beforeRouteUpdate
等
组件内守卫写在组件的选项里(Vue2)或组合式API的onBeforeRouteUpdate
(Vue3),比如beforeRouteEnter
在组件实例创建前触发,此时this
还没生成,所以要通过to
参数拿路由信息:
Vue2例子(组件内守卫):
<script> export default { name: 'Product', beforeRouteEnter(to, from, next) { // 这里不能用this,因为组件还没创建 // 但可以通过to拿到目标路由的query参数 const tab = to.query.tab || 'info' next(vm => { // vm是组件实例,创建后执行 vm.activeTab = tab // 把tab参数传给组件的activeTab }) } } </script>
Vue3例子(组合式API的onBeforeRouteUpdate
):
<script setup> import { onBeforeRouteUpdate } from 'vue-router' import { ref } from 'vue' const activeTab = ref('info') onBeforeRouteUpdate((to, from) => { // 路由参数变化时(比如从/product/1?tab=info 跳到 /product/1?tab=comment) activeTab.value = to.query.tab || 'info' }) </script>
组件内守卫的核心是:在路由进入、更新、离开的不同阶段,通过to
参数获取当前(或即将进入的)路由信息,配合组件逻辑做处理。
非组件场景怎么拿当前路由?(比如工具函数、状态管理里)
有时候逻辑不在组件里,比如写在Vuex的action、Pinia的store,或者单独的工具函数里,这时候要获取当前路由,得导入路由实例,然后用router.currentRoute
。
Vue3 例子(Pinia里获取当前路由):
假设在store/user.js
里,需要根据当前路由判断是否要跳转:
import { defineStore } from 'pinia' import { useRouter } from 'vue-router' export const useUserStore = defineStore('user', { actions: { logout() { // 清除登录态... const router = useRouter() const currentRoute = router.currentRoute.value // 注意是ref,要.value // 如果当前在个人中心页面,退出后跳首页;否则跳登录页 if (currentRoute.path === '/profile') { router.push('/') } else { router.push('/login') } } } })
单独工具函数例子:
写一个checkRouteAuth.js
工具函数,判断当前路由是否需要权限:
import { useRouter } from 'vue-router' export function checkRouteAuth() { const router = useRouter() const currentRoute = router.currentRoute.value return currentRoute.meta.requireAuth || false }
然后在组件里用:
<script setup> import { checkRouteAuth } from '@/utils/checkRouteAuth' import { onMounted } from 'vue' onMounted(() => { const needAuth = checkRouteAuth() if (needAuth && !isLogin()) { // 假设isLogin是判断登录的函数 // 跳登录页... } }) </script>
这里要注意:router.currentRoute
在Vue3中是响应式的ref
对象,所以必须通过.value
才能拿到最新的路由对象,如果直接写router.currentRoute.path
,拿到的是初始值,不会随路由变化更新。
拿到当前路由能干嘛?这些场景超实用
获取当前路由不是目的,而是为了解决实际业务问题,举几个常见场景,理解了这些,你就知道为啥要费心思拿路由信息了:
页面权限控制
后台管理系统很典型:不同角色(管理员、普通员工)能访问的页面不一样,在路由的meta
里配role
,然后结合全局守卫或组件内逻辑判断:
// 路由配置 { path: '/admin/dashboard', name: 'AdminDashboard', component: AdminDashboard, meta: { requireAuth: true, role: ['admin'] // 只有管理员能进 } } // 全局守卫判断 router.beforeEach((to, from, next) => { if (to.meta.role) { const userRole = store.state.user.role if (to.meta.role.includes(userRole)) { next() } else { next('/403') // 权限不足页面 } } })
动态导航高亮
网站头部或侧边栏的菜单,需要根据当前路由“高亮”对应的选项,比如用v-for
循环菜单,根据route.name
匹配:
<template> <nav> <ul> <li v-for="menu in menuList" :key="menu.name" :class="{ active: route.name === menu.name }" > {{ menu.title }} </li> </ul> </nav> </template> <script setup> import { useRoute } from 'vue-router' import { ref } from 'vue' const route = useRoute() const menuList = ref([ { name: 'Home', title: '首页' }, { name: 'About', title: '关于我们' }, { name: 'Contact', title: '联系我们' } ]) </script> <style scoped> .active { color: red; } </style>
埋点统计用户行为
比如统计用户在每个页面的停留时间,进入页面时记录时间,离开时计算时长并上报:
<script setup> import { useRoute, onBeforeRouteLeave } from 'vue-router' import { onMounted } from 'vue' const route = useRoute() let enterTime = 0 onMounted(() => { enterTime = Date.now() // 记录进入时间 }) onBeforeRouteLeave((to, from) => { const stayTime = Date.now() - enterTime // 上报停留时间:当前路由path + 时长 reportStayTime(from.path, stayTime) }) function reportStayTime(path, time) { // 调用接口上报, // axios.post('/api/analytics', { path, time }) } </script>
页面级缓存与销毁
用keep-alive
缓存组件时,有时候需要根据路由决定是否缓存,比如列表页需要缓存,详情页不需要:
<template> <router-view v-slot="{ Component }"> <keep-alive :include="keepAliveComponents"> <component :is="Component" /> </keep-alive> </router-view> </template> <script setup> import { useRoute, watch } from 'vue-router' import { ref } from 'vue' const keepAliveComponents = ref([]) const route = useRoute() watch( () => route.name, (newName) => { // 假设列表页路由name是'ProductList',需要缓存 if (newName === 'ProductList') { keepAliveComponents.value = ['ProductList'] } else { keepAliveComponents.value = [] } }, { immediate: true } ) </script>
常见问题:为啥有时候拿不到路由?咋解决?
实际开发中,获取路由容易踩坑,列几个高频问题和解法:
报错“this.$route is undefined”
原因:组件不是路由组件(即没有通过<router-view>
渲染,而是直接用<MyComponent>
写死在模板里),路由实例的$route
、$router
只会注入到路由组件中。
解决:确保组件是通过路由配置渲染的(在routes
里配了对应的component
),如果是普通组件需要路由信息,就用useRoute()
(Vue3)或者把路由信息通过props
传进去。
路由参数变了,但组件没刷新
原因:Vue路由默认复用相同组件(比如从/user/1
跳到/user/2
,组件实例不会销毁重建),所以created
、mounted
这些钩子不会重新执行,导致拿不到新参数。
解决:
-
Vue2:用
beforeRouteUpdate
组件内守卫,监听参数变化; -
Vue3:用
onBeforeRouteUpdate
组合式API,或者watch
路由的params
/query
:<script setup> import { useRoute, watch } from 'vue-router' const route = useRoute() watch( () => route.params.id, (newId) => { fetchData(newId) // 参数变化时重新请求数据 }, { immediate: true } ) </script>
router.currentRoute.value
拿的是旧值
原因:在Vue3中,router.currentRoute
是ref
对象,必须在响应式上下文(如setup
、生命周期钩子、watch
回调)里访问.value
,如果在普通函数或异步回调里直接拿,可能因为时机问题拿到旧值。
解决:确保访问router.currentRoute.value
时,是在响应式环境中,比如用watch
监听router.currentRoute
,或者在setup
里先赋值给变量:
const router = useRouter() const currentRoute = computed(() => router.currentRoute.value) // 然后用currentRoute.value.path
获取vue-router当前路由的核心思路是:根据场景选方法(组件内用$route/useRoute,守卫里用to参数,非组件场景用router.currentRoute),理解路由对象的结构,再结合业务场景(权限、导航、埋点等)做逻辑扩展,只要把这些细节理清楚,不管是Vue2还是Vue3项目,都能灵活拿到当前路由,让页面逻辑更丝滑~要是你在开发中遇到其他路由相关的问题,评论区随时聊聊~
版权声明
本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。