useRoute 获取不到路由参数
在 Vue 3 中,onMounted 中通过 useRoute 获取不到路由参数的问题,根源在于组件生命周期时序、路由初始化流程与响应式依赖注入的冲突。以下从源码层面分析原因和解决方案:
⚙️ 一、核心源码机制分析
1. useRoute 的响应式依赖注入原理
useRoute 依赖 Vue Router 的全局注入上下文(inject(routerKey))。在组件 setup 阶段,Vue Router 通过 provide(routerKey, router) 注入路由实例 。
- 关键点:
useRoute必须在同步的setup或<script setup>中调用,才能正确获取注入的上下文。若在异步回调(如onMounted中的setTimeout)中调用,可能因组件实例已卸载而无法获取注入值 。
2. 组件生命周期的时序冲突
onMounted的异步回调延迟执行:
当onMounted中的异步操作(如await fetchData())完成后,若父组件已触发reset或重新渲染,当前组件实例可能已被销毁,导致useRoute的依赖注入失效 。javascriptonMounted(async () => { await fetchData() // 异步操作延迟 const route = useRoute() // 此时组件实例可能已被重置 })- 源码中的重置逻辑:
Vue 3 的组件卸载流程会清除响应式依赖(如inject的上下文)。若异步操作完成时组件已卸载,useRoute将返回undefined。
3. Vue Router 的组件复用策略
Vue Router 默认复用同一组件(例如从 /user/1 跳转到 /user/2),仅更新路由参数而不触发组件重建。此时 onMounted 不会重新执行,但 route 对象会响应式更新 。
- 若父组件强制重置子组件(如通过
v-if),子组件实例销毁重建,异步回调中的useRoute可能因注入丢失而失败 。
🔧 二、解决方案与最佳实践
1. 在同步上下文中获取路由对象
将 useRoute 移至 setup 顶层,通过 watch 监听参数变化:
javascript
import { useRoute } from 'vue-router'
import { watch } from 'vue'
const route = useRoute() // 同步获取响应式 route 对象
watch(
() => route.params.id,
newId => {
// 处理参数变化
},
{ immediate: true }
) // 立即执行以捕获初始值
优势:避免依赖异步时序,直接利用响应式系统 。
2. 使用导航守卫替代生命周期钩子
对于同一组件内的参数更新,使用 onBeforeRouteUpdate:
javascript
import { onBeforeRouteUpdate } from 'vue-router'
onBeforeRouteUpdate(to => {
console.log('新参数:', to.params.id) // 直接访问最新参数
})
适用场景:路由跳转时组件复用的情况 。
3. 避免在异步回调中调用 useRoute
若必须在 onMounted 中处理异步逻辑,提前保存 route 引用:
javascript
const route = useRoute() // 同步保存
onMounted(async () => {
await fetchData()
console.log(route.params.id) // 使用已保存的响应式引用
})
原理:route 是响应式对象,其属性变更会自动更新 。
4. 强制组件重建以触发生命周期
当父组件需要重置子组件时,可通过 key 绑定路由参数强制重建:
vue
<template>
<ChildComponent :key="route.params.id" />
</template>
效果:参数变化时子组件销毁重建,触发完整的生命周期 。
💎 三、总结:问题根源与规避策略
| 关键因素 | 导致问题原因 | 规避方案 |
|---|---|---|
useRoute 依赖注入丢失 | 异步回调执行时组件实例已销毁 | 在 setup 顶层同步获取 route |
| 组件复用策略 | onMounted 不重复触发,参数更新无感知 | 使用 onBeforeRouteUpdate 监听 |
| 父组件重置子组件 | 子组件实例销毁导致异步回调上下文失效 | 提前保存 route 或绑定 key |
源码设计结论:
Vue Router 的useRoute高度依赖同步的依赖注入机制,而 Vue 组件的卸载是同步过程。异步操作(如onMounted中的await)完成后,若组件已被重置,注入的路由上下文会被清除,导致useRoute返回undefined。因此,务必在同步上下文中获取路由对象,再在异步逻辑中使用其引用。