三亩地 三亩地SAN MU DI · CODE DIARY
ARTICLE DETAIL

日记详情

真实记录编程学习的某一天,欢迎挑你感兴趣的翻一翻。

为什么 router 不会出现赋值时没值,之后才有值的情况?

为什么 router 不会出现赋值时没值,之后才有值的情况?

事情的起因来自一段router给ref变量赋值代码,由此产生一个问题,为什么这段代码是有效的。而不会出现在data被赋值时,router的query是空的,从而导致data赋值失效的情况

const data: any = ref({status: router.currentRoute.value.query.status? Number(...): '',
})

这触及到 Vue 路由的初始化时机和生命周期。


结论

setup() 执行时,路由已经完全初始化好了,包括当前路由的 query、params、path 等信息。所以在 setup 顶层(也就是 <script setup> 里)直接同步取值,100% 能拿到。


为什么?看 Vue 应用的启动流程

1. 应用启动顺序

// main.ts
import { createApp } from 'vue'
import { createRouter } from 'vue-router'
import App from './App.vue'const router = createRouter({ /* ... */ })const app = createApp(App)
app.use(router)  // ① 注册路由插件await router.isReady()  // ② 等待路由就绪(关键)`app.mount('#app')  // ③ 才开始渲染组件

关键点router.isReady() 会等待首次导航完成,确保当前路由(currentRoute)已经被正确解析

即使没显式调用 isReady(),Vue Router 内部也会在挂载组件前完成路由匹配。

2. 组件挂载时机

  1. Vue 应用启动
  2. 路由初始化 → 解析当前 URL → 填充 currentRoute(包含 query、params)
  3. 匹配到对应组件
  4. 执行组件的 setup() ← 此时 router.currentRoute.value 已经有完整数据
  5. 渲染模板

当你的 setup() 开始执行时,路由信息已经是"现成的"了,不存在"还没赋值"的中间态。


类比理解

把路由想象成"快递信息":

// ❌ 错误想象:先有组件,后填快递信息
const formData = ref({ ... }) // 此时快递还没到
// 然后才知道收件人地址// ✅ 实际流程:快递到了才开始拆包
// 路由信息已经填好 → 组件才被创建 → setup 执行时直接取值

真正会"取不到"的场景(对比理解)

场景 1:在 import 阶段就调用(❌ 错误)

import { useRouter } from 'vue-router'const router = useRouter()  // ❌ 在模块顶层调用,此时还没有 Vue 实例
const id = router.currentRoute.value.query.id  // 报错:router is null
useRouter() 必须在 setup() 内部调用,否则拿不到注入的实例。

场景 2:异步等待中路由发生了变化

const router = useRouter()const oldId = router.currentRoute.value.query.id  // ✅ 当前路由的 idsetTimeout(() => {router.push('/other?id=999')  // 路由跳走了const newId = router.currentRoute.value.query.id  // ✅ 999(新路由的 id)// 但 oldId 还是之前的值(已经赋给变量了)
}, 1000)

变量赋值是一次性快照,路由变了不会自动更新非响应式变量。

场景 3:异步路由(懒加载)+ 路由守卫阻塞

const router = createRouter({routes: [{path: '/order',component: () => import('./OrderList.vue')  // 懒加载}]
})router.beforeEach(async (to, from, next) => {await fetchPermission()  // 异步守卫next()
})

但即便如此,当组件最终被加载和挂载时,currentRoute 已经是 to 路由了,依然能正确取到 query。


你的代码为什么稳定能取到值?

const data: any = ref({status: router.currentRoute.value.query.status? Number(...): '',
})

执行时机:

  1. ✅ 用户访问 /list?status=2
  2. ✅ Vue Router 解析 URL → currentRoute.value.query =
  3. ✅ 加载并渲染 List.vue 组件
  4. ✅ 执行 setup() → 这行代码同步执行
  5. ✅ 此时 currentRoute.value.query.status === '2',正常赋值
    整个过程是同步的、有序的,路由数据已经"就位"了。

一句话总结
Vue Router 保证:组件的 setup() 执行时,router.currentRoute.value 一定包含了与当前 URL 匹配的完整路由信息。 这是路由库的设计契约,不需要担心时序问题。


真正需要担心的场景

只有以下两种情况要用响应式处理:

1. 路由参数变化但组件复用(同一个路由,参数变了)

// /list?id=1 → /order/list?id=2
// 组件不会重新挂载,setup 不会重新执行!const route = useRoute()// ❌ 这样取,路由变了不会更新
const id = route.query.id// ✅ 用 computed 或 watch
const id = computed(() => route.query.id)watch(() => route.query.id, (newId) => {fetchData(newId)
})

2. 需要响应式追踪

const route = useRoute()// 模板里直接用 route.query.id 是响应式的
// 但赋值给普通变量后就丢失响应式了
你的代码可能存在的隐患const data = ref({status: router.currentRoute.value.query.status ? Number(...) : '',
})

如果用户在同一个路由页面切换 query 参数(比如点击不同的 tab 改变 URL),data.status 不会自动更新,因为这只是初始化时的一次性赋值。

如果需要响应式跟随路由变化,要加 watch:


watch(() => router.currentRoute.value.query.status,(newVal) => {formData.value.status = newVal ? Number(newVal) : ''}
)

但99% 的列表查询场景,初始化取一次值就够了,不需要响应式跟随。

← 返回列表