路由参数
基本用法
使用动态路径参数(Dynamic Segment)以冒号 : 开头:
javascript
const routes = [
// 动态路径参数以冒号开头
{
path: '/user/:id',
component: User
}
]这样,/user/1、/user/2、/user/abc 都将映射到相同的路由。
访问路由参数
在组件中通过 $route.params 访问参数:
Vue SFC
<template>
<div>
<h1>用户详情</h1>
<p>用户 ID:{{ $route.params.id }}</p>
</div>
</template>
<script>
export default {
name: 'User',
created() {
console.log('用户 ID:', this.$route.params.id)
}
}
</script>多个参数
可以在一个路由中设置多个动态参数:
javascript
const routes = [
{
// 匹配 /user/123/profile 或 /user/123/posts
path: '/user/:id/:section',
component: UserDetail
}
]Vue SFC
<template>
<div>
<p>用户 ID:{{ $route.params.id }}</p>
<p>页面部分:{{ $route.params.section }}</p>
</div>
</template>URL 匹配示例:
| URL | params |
|---|---|
/user/123/profile | { id: '123', section: 'profile' } |
/user/456/posts | { id: '456', section: 'posts' } |
使用 props 解耦
使用 props: true 将路由参数作为组件 props 传入,实现组件与路由解耦:
路由配置
javascript
const routes = [
{
path: '/user/:id',
component: User,
props: true // 启用 props
}
]组件定义
Vue SFC
<template>
<div>
<h1>用户详情</h1>
<p>用户 ID:{{ id }}</p>
</div>
</template>
<script>
export default {
name: 'User',
props: {
id: {
type: String,
required: true
}
}
}
</script>优点
- 组件可以在任何地方使用,不依赖路由
- 更容易进行单元测试
- props 可以定义类型和验证规则
Props 的多种模式
对象模式
直接传递静态值:
javascript
const routes = [
{
path: '/promotion',
component: Promotion,
props: { showBanner: true }
}
]函数模式
创建函数返回 props:
javascript
const routes = [
{
path: '/search',
component: Search,
props: route => ({
query: route.query.q,
page: parseInt(route.query.page) || 1
})
}
]URL 示例:/search?q=vue&page=2
Vue SFC
<template>
<div>
<p>搜索关键词:{{ query }}</p>
<p>当前页:{{ page }}</p>
</div>
</template>
<script>
export default {
props: ['query', 'page']
}
</script>响应路由参数变化
问题
当使用动态路由时,如果只是参数变化(如 /user/1 → /user/2),组件实例会被复用,生命周期钩子不会重新执行。
javascript
export default {
created() {
// 只会在第一次进入时执行
console.log('组件创建')
this.fetchUser()
},
methods: {
fetchUser() {
// 获取用户数据
const id = this.$route.params.id
// API 调用...
}
}
}解决方案
方式一:监听 $route
javascript
export default {
watch: {
'$route'(to, from) {
// 路由变化时重新获取数据
this.fetchUser()
}
},
methods: {
fetchUser() {
const id = this.$route.params.id
console.log('获取用户:', id)
}
}
}方式二:使用导航守卫
javascript
export default {
beforeRouteUpdate(to, from, next) {
// 在当前路由改变,但组件被复用时调用
this.fetchUser(to.params.id)
next()
},
methods: {
fetchUser(id) {
console.log('获取用户:', id)
}
}
}方式三:使用不同的路由 key(不推荐)
Vue SFC
<router-view :key="$route.fullPath" />注意
使用 :key 会导致组件完全销毁重建,可能影响性能,谨慎使用。
完整示例
Vue SFC
<template>
<div class="user-detail">
<h2>用户详情</h2>
<p v-if="loading">加载中...</p>
<div v-else>
<p>用户 ID:{{ user.id }}</p>
<p>用户名:{{ user.name }}</p>
<p>邮箱:{{ user.email }}</p>
</div>
</div>
</template>
<script>
export default {
name: 'UserDetail',
props: ['id'],
data() {
return {
loading: false,
user: null
}
},
watch: {
// 监听 id 变化(当使用 props 时)
id: {
immediate: true,
handler(newId) {
this.fetchUser(newId)
}
}
},
methods: {
async fetchUser(id) {
this.loading = true
try {
// 模拟 API 调用
const response = await fetch(`/api/user/${id}`)
this.user = await response.json()
} catch (error) {
console.error('获取用户失败:', error)
} finally {
this.loading = false
}
}
}
}
</script>捕获所有路由
基本用法
使用通配符 * 捕获所有未匹配的路由,常用于 404 页面:
javascript
const routes = [
{
path: '*',
component: NotFound
}
]捕获带参数的路由
javascript
const routes = [
// 匹配所有以 `/user-` 开头的路径
{
path: '/user-*',
component: UserNotFound
},
// 匹配所有路径(放在最后作为 404)
{
path: '*',
component: NotFound
}
]访问参数:
javascript
// 访问 /user-admin
this.$route.params.pathMatch // 'admin'
// 访问 /user-123/profile
this.$route.params.pathMatch // '123/profile'404 页面示例
Vue SFC
<template>
<div class="not-found">
<h1>404</h1>
<p>页面未找到</p>
<p>路径:{{ $route.params.pathMatch }}</p>
<router-link to="/">返回首页</router-link>
</div>
</template>javascript
const routes = [
// 正常路由...
{ path: '/', component: Home },
{ path: '/about', component: About },
// 404 路由(必须放在最后)
{
path: '*',
component: () => import('@/views/NotFound.vue')
}
]高级匹配模式
Vue Router 使用 path-to-regexp 作为路径匹配引擎,支持高级匹配模式。
可重复参数
使用 +(一个或多个)、*(零个或多个)、?(零个或一个)修饰参数:
javascript
const routes = [
// 匹配 /user, /user/123, /user/123/456 等
{
path: '/user/:id+',
component: User
},
// 匹配 /list, /list/1, /list/1/2 等
{
path: '/list/:id*',
component: List
},
// 匹配 /user 或 /user/123
{
path: '/user/:id?',
component: User
}
]匹配示例:
| 路由 | URL | params |
|---|---|---|
/user/:id+ | /user/123 | { id: '123' } |
/user/:id+ | /user/123/456 | { id: ['123', '456'] } |
/user/:id* | /user | { id: undefined } 或 {} |
/user/:id* | /user/123 | { id: '123' } |
/user/:id? | /user | { id: undefined } |
/user/:id? | /user/123 | { id: '123' } |
自定义正则
使用括号 () 添加自定义正则约束:
javascript
const routes = [
// 只匹配数字
{
path: '/user/:id(\\d+)',
component: User
},
// 只匹配字母
{
path: '/category/:name([a-z]+)',
component: Category
},
// 匹配特定格式
{
path: '/order/:id(\\d{6})', // 6位数字
component: Order
}
]匹配示例:
| 路由 | URL | 是否匹配 |
|---|---|---|
/user/:id(\\d+) | /user/123 | ✅ |
/user/:id(\\d+) | /user/abc | ❌ |
/category/:name([a-z]+) | /category/tech | ✅ |
/category/:name([a-z]+) | /category/tech123 | ❌ |
/order/:id(\\d{6}) | /order/123456 | ✅ |
/order/:id(\\d{6}) | /order/12345 | ❌ |
可重复的自定义正则
javascript
const routes = [
// 匹配一个或多个数字段
{
path: '/segments/:id(\\d+)+',
component: Segments
}
]URL 示例:/segments/1/2/3
javascript
this.$route.params.id // ['1', '2', '3']匹配优先级
当多个路由匹配同一个 URL 时,按以下规则确定优先级:
- 更具体的路由优先
- 静态路由优先于动态路由
- 定义顺序:先定义的优先
优先级示例
javascript
const routes = [
// 静态路由 - 最高优先级
{
path: '/user/admin',
component: UserAdmin
},
// 动态路由 - 较低优先级
{
path: '/user/:id',
component: UserDetail
},
// 通配路由 - 最低优先级
{
path: '/user-*',
component: UserNotFound
}
]匹配结果:
| URL | 匹配路由 |
|---|---|
/user/admin | /user/admin(静态路由) |
/user/123 | /user/:id(动态路由) |
/user-profile | /user-*(通配路由) |
避免歧义的最佳实践
javascript
const routes = [
// ✅ 好的做法:具体路由在前
{ path: '/user/admin', component: UserAdmin },
{ path: '/user/profile', component: UserProfile },
{ path: '/user/:id', component: UserDetail },
{ path: '*', component: NotFound }
]
// ❌ 不好的做法:通配路由在前
const badRoutes = [
{ path: '*', component: NotFound }, // 会匹配所有!
{ path: '/user/:id', component: UserDetail } // 永远不会被匹配
]完整示例
用户管理路由配置
javascript
// router/modules/user.js
export default [
{
path: '/user',
component: () => import('@/views/user/Layout.vue'),
children: [
{
path: '',
name: 'UserList',
component: () => import('@/views/user/List.vue')
},
{
// 匹配数字 ID
path: ':id(\\d+)',
name: 'UserDetail',
component: () => import('@/views/user/Detail.vue'),
props: true
},
{
// 匹配特定字符串
path: 'create',
name: 'UserCreate',
component: () => import('@/views/user/Create.vue')
},
{
// 匹配数字 ID 的编辑页面
path: ':id(\\d+)/edit',
name: 'UserEdit',
component: () => import('@/views/user/Edit.vue'),
props: true
}
]
}
]URL 匹配结果
| URL | 匹配路由 | params |
|---|---|---|
/user | UserList | - |
/user/123 | UserDetail | { id: '123' } |
/user/create | UserCreate | - |
/user/123/edit | UserEdit | { id: '123' } |
/user/abc | 无匹配(需要 404 处理) | - |
最佳实践
1. 使用 props 解耦组件
javascript
// ✅ 推荐
{
path: '/user/:id',
component: User,
props: true
}
// ❌ 不推荐:组件内直接使用 $route.params2. 合理使用正则约束
javascript
// 只匹配数字 ID
{
path: '/user/:id(\\d+)',
component: User
}
// 只匹配特定格式
{
path: '/order/:sn(ORD\\d{8})', // ORD + 8位数字
component: Order
}3. 正确处理参数变化
javascript
export default {
watch: {
'$route'(to, from) {
// 参数变化时重新获取数据
if (to.params.id !== from.params.id) {
this.fetchData()
}
}
}
}4. 404 路由放在最后
javascript
const routes = [
// ...其他路由
// 404 必须放在最后
{ path: '*', component: NotFound }
]5. 避免过度嵌套
javascript
// ❌ 不推荐:过多参数嵌套
{
path: '/user/:userId/post/:postId/comment/:commentId',
component: Comment
}
// ✅ 推荐:使用查询参数或简化路径
{
path: '/comment/:commentId',
component: Comment
}常见问题
1. 参数变化时组件不更新?
使用 watch 监听 $route 或 beforeRouteUpdate 守卫。
2. 如何验证参数格式?
使用自定义正则约束:
javascript
{
path: '/user/:id(\\d+)', // 只匹配数字
component: User
}3. 如何获取通配符匹配的内容?
javascript
// 路由配置
{
path: '/user-*',
component: User
}
// 访问 /user-admin
this.$route.params.pathMatch // 'admin'4. 如何处理可选参数?
javascript
// 使用 ? 修饰符
{
path: '/search/:keyword?', // keyword 可选
component: Search
}
// 匹配 /search 和 /search/vue调试技巧
查看当前路由信息
javascript
// 在组件中
console.log('当前路由:', this.$route)
console.log('路由参数:', this.$route.params)
console.log('匹配的路由记录:', this.$route.matched)路由匹配调试
javascript
// 检查路由是否匹配
const match = this.$router.match('/user/123')
console.log('匹配结果:', match)