Code前端首页关于Code前端联系我们

一、为啥推荐用 props 传路由参数?

terry 10小时前 阅读数 13 #Vue
文章标签 路由参数 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;如果没传 pagepage 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.sortroute.query.page,处理后传给组件,组件里直接用 sortpage 这两个 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前端网发表,如需转载,请注明页面地址。

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

热门