Code前端首页关于Code前端联系我们

先搞懂,Vue Router与当前路由是什么?

terry 3小时前 阅读数 5 #Vue
文章标签 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>

这里要注意两个点:

  1. this 的指向:必须在Vue组件的实例上下文里用(比如methodscomputed、生命周期钩子),如果用箭头函数定义方法,this 会丢失,变成undefined
  2. 路由对象是响应式的:当路由变化时(比如从/user/1跳到/user/2),this.$route 会自动更新,所以基于它的计算属性、绑定的class也会自动响应变化~

组合式API里用 useRoute

如果你的项目是Vue 3 + Composition API(尤其是用<script setup>语法糖),获取路由得用组合式API提供的useRoute,步骤很简单:

  1. vue-router里导入useRoute
  2. 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) // 可能还是原来的路径!

解决方法有两种:

  1. router.push的回调:router.push支持传回调,导航完成后执行:
router.push('/user/123', () => {
  console.log(this.$route.path) // 此时是新路径'/user/123'
})
  1. 结合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.$routeundefined

大概率是这两个原因:

  • 组件没被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前端网发表,如需转载,请注明页面地址。

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

热门