Vue3 如何在 App.vue 中访问 query 参数及 query 参数 undefined 的原因

本文以基于 Vite 2 构建的项目为基础进行说明,其他环境可能存在差异请自行判断。

当你在 App.vue 中访问 route.query.id 时,可能会遇到传了值但获取到的是 undefined 的情况:

1
2
3
4
5
6
7
8
<script setup>
// App.vue
// 访问地址为 /anypath?id=123
import { useRoute } from 'vue-router'
const route = useRoute()

console.log(route.query.id) // undefined
</script>

原因是 Vue Router 导航是异步的,在 App.vue 中,不论是在 setup 还是 onMounted 生命周期,还未解析路由所以都无法获取。

如果需要在 setup 拿到 id 应该怎么做呢?你可以使用 isReady API, 像这样:

1
2
3
4
5
6
7
8
9
10
11
<script setup>
// App.vue
// 访问地址为 /anypath?id=123
import { useRoute, useRouter } from 'vue-router'
const route = useRoute()
const router = useRouter()

console.log(route.query.id) // undefined
await router.isReady()
console.log(route.query.id) // 123
</script>

需要注意的是,这里涉及到另一个知识点:顶层 await

官方说明如下:

async setup() 必须与 Suspense 组合使用,Suspense 目前还是处于实验阶段的特性。我们打算在将来的某个发布版本中开发完成并提供文档 - 如果你现在感兴趣,可以参照 tests 看它是如何工作的。

Suspense 是实验性的:

Suspense 是一个试验性的新特性,其 API 可能随时会发生变动。特此声明,以便社区能够为当前的实现提供反馈。
生产环境请勿使用。

目前这是一个实验性功能,所以顶层 await 还是避免在生产环境中使用,所以可以这样写:

1
2
3
4
5
6
7
8
9
10
11
<script setup>
// App.vue
// 访问地址为 /anypath?id=123
import { useRouter } from 'vue-router'
const router = useRouter()

console.log(route.query.id) // undefined
router.isReady().then(() => {
console.log(route.query.id) // 123
})
</script>

别忘了 Vue 的响应式思维,如果你只是为了确保拿到值再渲染视图,甚至可以这样:

1
2
3
4
5
6
7
8
9
10
<script setup>
// App.vue
// 访问地址为 /anypath?id=123
</script>

<template>
<div v-if="$route.query.id">
<router-view />
</div>
</template>

你没看错~ 不需要任何 JavaSscript 代码,因为在模板中可以直接通过 $route$router 访问路由,见 Vue Router and the Composition API

当然,你可能需要处理一点其他的业务逻辑:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<script setup>
// App.vue
// 访问地址为 /anypath?id=123
import { useRoute, useRouter } from 'vue-router'
import { useStore } from 'vuex'

const route = useRoute()
const router = useRouter()
const store = useStore()

router.isReady().then(() => {
// Do somethings.. For example to save to global state:
store.commit('setID', route.query.id)
})
</script>

<template>
<div v-if="$route.query.id">
<router-view />
</div>
</template>

这里用 Vuex 只是为了演示方便理解,个人认为如果不是特别大型且复杂的业务或项目,在 Vue 3 中完全没有必要上 Vuex, 利用 Refs 可以轻易实现全局状态管理。

你还可以结合 ref, computed 等灵活实现更多搭配解决实际需求,因为超出了本文范畴就不在此详细介绍了。

References