一、为啥推荐用 props 传路由参数?
不少刚接触Vue Router的同学,在页面跳转传参时,对“push 结合 props 传参”有点懵:明明知道 query、params 能传参,为啥要用 props?push 时咋配合 props 把参数传给组件?今天就把这些问题拆碎了讲明白~
先想个场景:列表页点某条数据,跳转到详情页展示数据 ID,常规做法是用 `$route.params.id` 拿参数,但这样组件和路由强绑定了——测试时得模拟 `$route`,复用组件也得带着路由逻辑,很不灵活。用 props 传参就不一样了:路由把参数“解耦”成组件的 props,组件里只需要声明 props: ['id'],完全不用管路由怎么传的,这样做有三个核心优势:
- 组件复用性高:比如把详情组件给其他路由用,或者在非路由场景(比如弹窗)里用,直接传 props 就行,不用改内部逻辑。
- 测试更简单:单元测试时,直接给组件传
:id="123"就能测,不用模拟$route这种“路由专属”的东西。 - 代码逻辑更清晰:路由负责“路由层”的参数解析,组件只负责“视图层”的渲染,分工明确,后期维护一眼能看懂。
对比 query/params 直接从 $route 拿值的写法,props 传参相当于给组件加了一层“参数过滤”,把路由细节和组件逻辑隔开,这也是 Vue 组件化思想的体现~
路由配置里咋开启 props 传参?
Vue Router 的路由规则里,有个 props 选项,它决定了“怎么把路由参数转成组件的 props”,这个选项有 布尔值、对象、函数 三种玩法,先一个个拆:
布尔值模式:把动态路由的 params 直接当 props
适合“动态路由 + 参数透传”的场景,比如路由配置长这样:
{
path: '/user/:userId',
component: UserDetail,
props: true
}
这里 props: true 的意思是:把路由中 :userId 对应的 params(也就是 URL 里的动态段),直接传给组件的 props。
组件里只需要声明:
export default {
props: ['userId'], // 对应路由里的 :userId
mounted() {
console.log(this.userId) // 就能拿到路由传的参数
}
}
这种模式的关键是 路由必须是动态路由(带 :参数),params 会自动映射成 props,如果路由是静态的(/user),开了 props: true 也没用,因为没 params 可以传~
对象模式:给组件传静态 props
如果想给组件传“固定值”,不管路由怎么变都不变,用对象模式。
{
path: '/about',
component: About,
props: { company: '字节', slogan: '激发创造' }
}
组件里声明 props: ['company', 'slogan'],不管怎么跳转到 /about,这两个值都是固定的。
这种模式适合“路由对应组件需要固定配置”的场景,比如官网关于页的品牌信息,不太会变,用对象模式传静态值很方便~
函数模式:灵活处理路由参数(params/query 都能玩)
如果想结合 params、query,甚至加自定义逻辑处理参数,就得用函数模式,路由配置里 props 是个函数,接收 route 对象(包含 params、query、path 等信息),返回一个对象作为 props。
举个实际例子:商品列表页,需要从 query 里拿 sort(排序方式)和 page(页码),但 page 要转成数字,且默认值为 1,路由配置可以这样写:
{
path: '/goods',
component: GoodsList,
props: (route) => ({
sort: route.query.sort,
page: parseInt(route.query.page) || 1
})
}
组件里声明 props: ['sort', 'page'],当用 router.push({ path: '/goods', query: { sort: 'price', page: '3' } }) 跳转时,组件拿到的 page 是数字 3;如果没传 page,page 1。
函数模式的灵活性在于:能整合 params、query,还能加逻辑处理(比如类型转换、默认值),特别适合复杂的列表筛选、多条件跳转场景~
push 跳转时咋配合 props 传参?
router.push 的参数可以是字符串路径,也可以是对象(包含 name、path、params、query 等),但要注意:push 本身并不直接传 props,而是通过路由的 props 配置,把 push 带的 params/query 转成 props,所以得结合上面的路由配置模式,分情况看怎么传:
布尔值模式下的 push 传参(动态路由场景)
路由配置是 props: true + 动态路由(:userId),这时候 push 要传 params 来匹配动态段。
比如列表页跳转到用户详情:
// 列表页的点击事件里
router.push({
name: 'userDetail', // 用命名路由更稳,避免 path 拼接出错
params: { userId: 123 }
})
路由配置:
{
name: 'userDetail',
path: '/user/:userId',
component: UserDetail,
props: true
}
组件里通过 props: ['userId'] 接收,就能拿到 123,这里的关键是 push 的 params 要和路由的动态段 :userId 对应上,否则 params 会丢(URL 里没显示,组件也拿不到)。
函数模式下的 push 传参(结合 query/params)
函数模式的路由配置,依赖 route 对象里的 params/query,push 时要传对应的 query 或 params。
比如之前的商品列表例子,想传排序和页码:
// 筛选栏点击“按价格排序”并跳转到第 2 页
router.push({
path: '/goods',
query: { sort: 'price', page: '2' }
})
路由的 props 函数会拿到 route.query.sort 和 route.query.page,处理后传给组件,组件里直接用 sort 和 page 这两个 props 就行,完全不用管路由层的 query 怎么处理~
对象模式下的 push 传参(静态值场景)
对象模式的 props 是“写死”在路由里的,push 传参不影响 props(因为 props 是静态对象),比如路由配置 props: { company: '字节' },不管 push 传啥,组件拿到的 company 都是“字节”。
这种场景下,push 主要负责“跳转到这个路由”,参数由路由自己控制,适合不需要动态传参的页面~
组件接收 props 要注意啥?
路由把参数转成 props 后,组件得“接住”这些值,否则白传了,这一步有两个关键点:
必须声明 props 选项
组件里要明确写 props: ['参数名'](或对象形式),否则 Vue 不会把路由传的参数绑定到组件实例上,比如动态路由传 userId,组件里得写:
export default {
props: {
userId: {
type: Number, // 类型校验,避免传错类型
required: true // 必传,因为路由里有动态段
}
},
mounted() {
console.log(this.userId) // 正确拿到值
}
}
如果没声明 props,直接用 this.userId 会报错,因为组件实例上根本没有这个属性~
处理可选参数(避免 undefined)
如果路由参数是“可选”的(query 里的参数可能不存在),要在 props 里加默认值,比如函数模式里的 page 参数,我们用了 parseInt(route.query.page) || 1 做默认值;如果是布尔值模式,动态路由的 params 是必传的(否则 URL 不匹配),所以可以用 required: true。
举个错误案例:路由是 /user/:userId?(userId 可选),props 开了 true,但组件里 props: { userId: Number } 没加默认值,这时候如果 push 没传 userId,组件里 this.userId undefined,可能导致渲染错误。
解决办法:要么让路由参数必传(去掉 ),要么在 props 里加默认值:
props: {
userId: {
type: Number,
default: 0 // 没传时默认用 0
}
}
实战:从列表到详情页的完整流程
光讲理论太虚,结合实际需求走一遍流程,你就懂了~
需求:文章列表页,点击某篇文章,跳转到详情页,展示文章 ID 和标题;详情页组件要复用(比如以后做弹窗预览),不能依赖 $route。
步骤 1:配置路由(用布尔值模式)
// router.js
const routes = [
{
path: '/article/:articleId',
name: 'ArticleDetail',
component: () => import('./views/ArticleDetail.vue'),
props: true // 开启 props 传参,把 :articleId 转成 props
}
]
步骤 2:列表页 push 跳转
列表页里,每个文章项的点击事件:
<!-- ArticleList.vue -->
<template>
<div>
<div
v-for="article in articleList"
:key="article.id"
@click="goToDetail(article)"
>
{{ article.title }}
</div>
</div>
</template>
<script>
export default {
data() {
return {
articleList: [
{ id: 1, title: 'Vue Router 传参指南' },
{ id: 2, title: 'Props 解耦的艺术' }
]
}
},
methods: {
goToDetail(article) {
this.$router.push({
name: 'ArticleDetail', // 用命名路由,避免 path 硬编码
params: { articleId: article.id } // 传动态参数
})
}
}
}
</script>
步骤 3:详情页接收 props 并渲染
<!-- ArticleDetail.vue -->
<template>
<div>
<h1>文章 ID:{{ articleId }}</h1>
<p>标题:{{ articleTitle }}</p>
</div>
</template>
<script>
export default {
props: {
articleId: {
type: Number,
required: true
},
articleTitle: {
type: String,
required: true
}
},
// 假设从接口拿标题,这里用计算属性模拟
computed: {
articleTitle() {
// 实际项目里可能调接口:axios.get(`/api/article/${this.articleId}`)
return `模拟标题_${this.articleId}`
}
}
}
</script>
这样一来,详情页完全不依赖 $route,以后要做弹窗预览,直接写 <ArticleDetail :article-id="3" /> 就能用,路由和组件彻底解耦~
常见坑点 & 解决办法
用 props 传参时,这些“小陷阱”很容易踩,提前避坑:
坑 1:路由 props 开了 true,但 push 没传 params
比如路由是 /user/:userId,props: true,但 push 时写成 this.$router.push('/user')(没传 userId),这时候 URL 会变成 /user(动态段丢了),组件里的 userId 也是 undefined。
解决:push 时必须传 params 匹配动态路由,或者把路由改成可选动态段(/user/:userId?),同时组件 props 加默认值。
坑 2:组件没声明 props,直接用 this.xxx
比如路由传了 userId,但组件里没写 props: ['userId'],直接用 this.userId,Vue 会报错“属性或方法未定义”。
解决:组件里必须显式声明 props,把要接收的参数列出来。
坑 3:函数模式下,query 参数类型不对
比如路由 props 函数里把 query.page 转成数字,但 push 时传的是字符串 page: 'abc',parseInt 后变成 NaN,导致组件逻辑出错。
解决:在 props 函数里加类型校验逻辑,
props: (route) => ({
page: route.query.page ? parseInt(route.query.page) : 1
})
或者在 push 前先处理参数类型,确保传给路由的是合法值。
props 传参让路由更优雅
Vue Router 里用 push 结合 props 传参,核心是通过路由的 props 选项,把路由参数(params/query)转成组件的 props,让组件和路由解耦。
- 简单场景(动态路由 + 透传参数)用布尔值模式,传 params 就行;
- 固定值场景用对象模式,路由里写死 props;
- 复杂场景(多参数、类型转换、默认值)用函数模式,灵活处理 route 对象里的参数;
这样做不仅让组件更纯净、复用性更强,还能让整个项目的路由逻辑和组件逻辑分工更清晰,下次写项目时,别再只知道 query/params 直接拿值啦,试试 props 传参,代码会清爽很多~
(如果还有疑问,命名路由和路径路由在 push 时传参有啥区别?”“props 函数里能结合 store 吗?”这些进阶问题,评论区喊我,咱们再展开唠~)
版权声明
本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。
code前端网


