64. 路由参数与查询参数
📖 概述
路由参数和查询参数是Vue Router中用于在路由间传递数据的重要机制。路由参数用于标识特定资源,通常出现在URL路径中;查询参数用于过滤、排序或配置资源,通常出现在URL末尾的查询字符串中。本集将深入讲解Vue Router 4.x中路由参数和查询参数的定义、配置、获取和使用,帮助你掌握在Vue 3应用中通过路由传递数据的完整技能。
✨ 核心知识点
1. 路由参数基础
什么是路由参数
- 路由参数是URL路径中的动态部分,用于标识特定资源
- 路由参数以冒号(:)开头,如
/user/:id中的:id - 路由参数的值会被解析到
route.params对象中 - 适用于标识唯一资源,如用户ID、产品ID等
配置路由参数
// src/router/index.ts
import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router'
const routes: Array<RouteRecordRaw> = [
{
path: '/user/:id',
name: 'user',
component: () => import('../views/UserView.vue')
},
{
path: '/product/:category/:id',
name: 'product',
component: () => import('../views/ProductView.vue')
},
{
// 可选参数,以?结尾
path: '/article/:id?',
name: 'article',
component: () => import('../views/ArticleView.vue')
},
{
// 零或多个参数,以*结尾
path: '/files/*',
name: 'files',
component: () => import('../views/FilesView.vue')
},
{
// 一个或多个参数,以+结尾
path: '/tags/:tag+',
name: 'tags',
component: () => import('../views/TagsView.vue')
}
]
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes
})
export default router2. 获取路由参数
在组件中获取路由参数
1. 使用Composition API
<script setup lang="ts">
import { useRoute } from 'vue-router'
const route = useRoute()
// 获取路由参数
const userId = route.params.id as string
const category = route.params.category as string
const tag = route.params.tag as string
// 响应式获取参数
console.log(route.params) // { id: '123' }
</script>2. 使用Options API
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
computed: {
userId() {
return this.$route.params.id
}
},
watch: {
'$route.params'(newParams, oldParams) {
// 监听路由参数变化
console.log('参数变化:', newParams, oldParams)
}
}
})
</script>路由参数的类型转换
<script setup lang="ts">
import { useRoute, watch } from 'vue-router'
const route = useRoute()
// 类型转换
const userId = parseInt(route.params.id as string)
const isActive = route.params.active === 'true'
// 监听参数变化并转换类型
watch(
() => route.params,
(newParams) => {
const id = parseInt(newParams.id as string)
// 使用转换后的参数
fetchUser(id)
},
{ deep: true }
)
async function fetchUser(id: number) {
// 异步请求用户数据
}
</script>3. 查询参数
什么是查询参数
- 查询参数是URL末尾的键值对,以?开头,如
/search?q=vue&sort=desc - 查询参数会被解析到
route.query对象中 - 适用于过滤、排序、分页等场景
- 查询参数是可选的,不影响路由匹配
配置和使用查询参数
// 编程式导航传递查询参数
router.push({
path: '/search',
query: { q: 'vue', sort: 'desc' }
})
// 声明式导航传递查询参数
<router-link :to="{ path: '/search', query: { q: 'vue' } }">搜索</router-link>在组件中获取查询参数
<script setup lang="ts">
import { useRoute, watch } from 'vue-router'
const route = useRoute()
// 获取查询参数
const searchQuery = route.query.q as string
const sortOrder = route.query.sort as string
const page = parseInt(route.query.page as string || '1')
// 监听查询参数变化
watch(
() => route.query,
(newQuery, oldQuery) => {
console.log('查询参数变化:', newQuery, oldQuery)
// 执行搜索或过滤操作
performSearch(newQuery)
},
{ deep: true }
)
function performSearch(query: Record<string, string>) {
// 执行搜索逻辑
}
</script>4. 路由参数与查询参数的对比
| 特性 | 路由参数 | 查询参数 |
|---|---|---|
| 语法 | /user/:id |
/search?q=vue |
| 位置 | URL路径中 | URL末尾 |
| 必填性 | 路由匹配时必填(除非配置为可选) | 可选 |
| 唯一性 | 用于标识唯一资源 | 用于过滤、排序等 |
| 历史记录 | 改变路由参数会创建新的历史记录 | 改变查询参数会创建新的历史记录 |
| 类型 | 字符串 | 字符串(需要手动转换) |
| 适用场景 | 用户详情、产品详情等 | 搜索、过滤、分页等 |
5. 高级路由参数技巧
1. 路由参数的默认值
// 使用props传递默认值
const routes: Array<RouteRecordRaw> = [
{
path: '/user/:id',
name: 'user',
component: UserView,
props: (route) => ({
id: route.params.id || 'default'
})
}
]2. 路由参数的验证
// 在组件中验证路由参数
<script setup lang="ts">
import { useRoute, useRouter } from 'vue-router'
const route = useRoute()
const router = useRouter()
const userId = parseInt(route.params.id as string)
// 验证参数
if (isNaN(userId) || userId <= 0) {
// 参数无效,重定向到404页面
router.push('/404')
}
</script>3. 路由参数的解构
<script setup lang="ts">
import { useRoute } from 'vue-router'
const route = useRoute()
// 解构路由参数
const { id, category } = route.params as {
id: string
category: string
}
// 解构查询参数
const { q, sort, page } = route.query as {
q: string
sort: string
page: string
}
</script>6. 路由参数与组件通信
1. 使用props传递路由参数
// 配置路由,将参数作为props传递
const routes: Array<RouteRecordRaw> = [
{
path: '/user/:id',
name: 'user',
component: UserView,
props: true // 将路由参数作为props传递给组件
},
{
path: '/search',
name: 'search',
component: SearchView,
// 将查询参数作为props传递
props: (route) => ({
query: route.query.q,
sort: route.query.sort
})
}
]<!-- UserView.vue -->
<script setup lang="ts">
interface Props {
id: string
}
const props = defineProps<Props>()
// 直接使用props.id,无需访问route.params
console.log(props.id)
</script>2. 路由参数与响应式数据
<script setup lang="ts">
import { ref, watch } from 'vue'
import { useRoute } from 'vue-router'
const route = useRoute()
const user = ref<User | null>(null)
// 监听路由参数变化,更新响应式数据
watch(
() => route.params.id,
async (newId) => {
if (newId) {
user.value = await fetchUser(newId as string)
}
},
{ immediate: true } // 立即执行
)
async function fetchUser(id: string) {
// 异步请求用户数据
}
</script>7. 动态路由与参数
1. 动态生成路由
// 根据权限动态生成路由
function generateRoutes(permissions: string[]) {
const dynamicRoutes: Array<RouteRecordRaw> = []
if (permissions.includes('admin')) {
dynamicRoutes.push({
path: '/admin/users/:id',
name: 'admin-user',
component: () => import('../views/AdminUserView.vue')
})
}
return dynamicRoutes
}2. 动态修改路由参数
// 动态修改路由参数,不触发导航
router.replace({
...router.currentRoute.value,
params: {
...router.currentRoute.value.params,
id: 'new-id'
}
})8. 路由参数与导航守卫
1. 在导航守卫中使用路由参数
// src/router/index.ts
router.beforeEach((to, from, next) => {
// 检查路由参数
if (to.name === 'user' && !to.params.id) {
next({ name: '404' })
return
}
// 验证参数格式
if (to.name === 'product') {
const id = parseInt(to.params.id as string)
if (isNaN(id)) {
next({ name: '404' })
return
}
}
next()
})2. 组件内守卫处理参数变化
<script setup lang="ts">
import { onBeforeRouteUpdate } from 'vue-router'
onBeforeRouteUpdate((to, from, next) => {
// 路由参数更新时处理
const newId = to.params.id as string
const oldId = from.params.id as string
if (newId !== oldId) {
// 参数变化,重新获取数据
fetchData(newId)
}
next()
})
function fetchData(id: string) {
// 获取数据
}
</script>📝 最佳实践
使用命名路由传递参数
- 优先使用命名路由+params的方式传递参数
- 避免使用字符串路径拼接,减少错误
路由参数类型安全
- 始终对路由参数进行类型检查和转换
- 使用TypeScript接口定义参数类型
监听路由参数变化
- 使用watch监听参数变化,及时更新数据
- 避免在组件初始化时只获取一次参数
使用props传递参数
- 将路由参数作为props传递给组件,提高组件复用性
- 使组件更独立,便于测试
参数验证
- 在导航守卫或组件中验证路由参数的有效性
- 对无效参数进行处理,如重定向到404页面
合理使用路由参数和查询参数
- 资源标识使用路由参数
- 过滤、排序等使用查询参数
避免过度使用动态路由
- 动态路由过多会增加路由匹配的复杂度
- 考虑使用查询参数替代部分动态路由
路由参数的默认值
- 为可选参数提供合理的默认值
- 使用props传递默认值,提高组件可用性
💡 常见问题与解决方案
路由参数变化但组件不更新
- 原因:组件已被缓存或未监听参数变化
- 解决方案:
- 使用watch监听路由参数变化
- 使用
onBeforeRouteUpdate守卫处理参数更新 - 确保组件没有被keep-alive缓存,或使用
activated钩子
路由参数类型错误
- 原因:未对参数进行类型转换
- 解决方案:
- 使用类型断言或类型转换
- 在组件中验证参数类型
- 使用TypeScript接口定义参数类型
查询参数丢失
- 原因:使用命名路由时,query参数不会自动保留
- 解决方案:
- 在导航时显式传递query参数
- 使用
router.push({ ...to, query: { ...to.query, newParam: 'value' } })
路由参数包含特殊字符
- 原因:URL中包含空格、中文等特殊字符
- 解决方案:
- 使用
encodeURIComponent编码参数 - 使用
decodeURIComponent解码参数
- 使用
路由匹配优先级问题
- 原因:路由配置顺序不当,导致优先级错误
- 解决方案:
- 将更具体的路由放在前面
- 避免通配符路由在前面
📚 进一步学习资源
🎯 课后练习
基础练习
- 配置带路由参数的路由
- 在组件中获取和使用路由参数
- 传递和获取查询参数
进阶练习
- 实现路由参数的类型转换和验证
- 使用props传递路由参数
- 监听路由参数变化,更新响应式数据
实战练习
- 构建一个产品详情页面,使用路由参数传递产品ID
- 实现一个搜索页面,使用查询参数进行过滤和排序
- 构建一个分页系统,使用查询参数传递页码
性能优化练习
- 优化路由参数变化时的数据获取
- 实现路由参数的缓存策略
- 测试不同路由参数配置的性能差异
通过本集的学习,你已经掌握了Vue Router 4.x中路由参数和查询参数的定义、配置、获取和使用,以及路由参数与组件通信、导航守卫的结合使用。在实际项目中,合理运用路由参数和查询参数,能够构建出功能完整、用户体验良好的单页应用。下一集我们将深入学习命名路由与命名视图,进一步提升Vue 3路由系统的使用能力。