{T}

路由参数

基本用法

使用动态路径参数(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 匹配示例:

URLparams
/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
  }
]

匹配示例:

路由URLparams
/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 时,按以下规则确定优先级:

  1. 更具体的路由优先
  2. 静态路由优先于动态路由
  3. 定义顺序:先定义的优先

优先级示例

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
/userUserList-
/user/123UserDetail{ id: '123' }
/user/createUserCreate-
/user/123/editUserEdit{ id: '123' }
/user/abc无匹配(需要 404 处理)-

最佳实践

1. 使用 props 解耦组件

javascript
// ✅ 推荐
{
  path: '/user/:id',
  component: User,
  props: true
}

// ❌ 不推荐:组件内直接使用 $route.params

2. 合理使用正则约束

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 监听 $routebeforeRouteUpdate 守卫。

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)