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

Vue Router里的query props该怎么用?要注意啥?

terry 14小时前 阅读数 11 #Vue

不少做前端开发的同学在玩Vue项目时,碰到Vue Router的query和props结合使用,总会犯嘀咕——这俩咋配合能让组件传参更顺?query props到底能解决啥问题?今儿咱就掰开揉碎了聊聊Vue Router里query props的那些事儿,从基础用法到实际场景,再到避坑技巧,一次讲明白。

query和props在Vue Router里分别是干啥的?

先把基础概念理清楚。query 是路由中 URL 里 后面跟着的参数,/page?name=张三&age=18 里,nameage query 参数,它属于路由信息($route)的一部分,作用更像“附加筛选条件”或者“页面状态标记”。

props 是 Vue 组件接收外部数据的核心方式,父组件通过 props 把数据传给子组件,子组件用 props 选项声明后就能用,这种方式让组件依赖更清晰,也方便测试(不用关心父组件咋来的,传数据就行)。

Vue Router 里搞了个“骚操作”:能把路由的 query 参数直接映射成组件的 props,这么做的好处是 让组件和路由解耦 —— 组件不用直接去读 this.$route.query,改成用 props 接收,后续换路由逻辑或者测试组件时,直接传模拟数据就行,灵活度拉满!

怎么把query参数转成组件props?

这得在路由配置里动刀子,Vue Router 给 props 配置提供了三种玩法:布尔值、对象、函数,咱重点看和 query 结合的场景:

布尔值模式(简单映射)

如果路由配置里写 props: true,Vue Router 会自动把 route.query 里的所有参数,一股脑传给组件当 props,举个栗子:

// 路由配置
{
  path: '/search',
  component: SearchComponent,
  props: true 
}
// 组件里声明props
export default {
  props: ['keyword', 'page'], // query里的keyword、page会被传进来
  mounted() {
    console.log(this.keyword, this.page); // 直接用props,不用碰$route
  }
}

这种方式适合 query 参数少、不需要额外处理的场景,但灵活性弱(没法改参数名、加默认值)。

函数模式(自定义处理)

要是想对 query 参数做“加工”(比如改名字、加默认值、转类型),用函数模式更爽,路由配置里的 props 可以是个函数,接收 route 对象当参数,返回要传给组件的 props 对象。

// 路由配置
{
  path: '/goods',
  component: GoodsList,
  props: (route) => {
    return {
      // query里的page是字符串,转成数字
      currentPage: Number(route.query.page) || 1, 
      // 给分类加默认值,没传就显示“全部”
      category: route.query.category || '全部' 
    };
  }
}
// 组件里声明props
export default {
  props: {
    currentPage: {
      type: Number,
      required: true
    },
    category: {
      type: String,
      default: '全部'
    }
  },
  created() {
    this.fetchGoods(this.currentPage, this.category); // 用props发请求
  }
}

这种方式能灵活处理 query 参数,比如类型转换、默认值、参数重命名,组件里的 props 也能更“干净”(不用兼容路由里的参数名)。

用query props有啥好处?

很多同学会疑惑:直接用 this.$route.query 也能拿参数,为啥非得绕个 props?这就得聊聊解耦、可维护、可测试这几个关键点了:

组件和路由解耦,测试更简单

如果组件里直接用 this.$route.query,测试时得模拟整个路由对象,特麻烦,但用 props 接收的话,测试组件时直接传假数据就行,比如测试 GoodsList 组件,不用管路由,直接传 { currentPage: 2, category: '手机' } 就能测,清爽!

参数处理集中化,组件更纯粹

路由配置里把 query 转 props 的逻辑(比如类型转换、默认值)集中处理,组件只负责“渲染/逻辑”,不用关心参数从哪来、咋处理的,相当于把“路由层”和“组件层”的职责分开,代码维护起来更顺。

符合单向数据流,Bug 好排查

Vue 里 props 是“父传子”的单向数据流,路由作为“父”把 query 处理后传给组件,组件里 props 变化能清晰追踪,要是直接用 $route,参数变化时组件可能没感知,容易埋 Bug;用 props 的话,配合 watch 或者组件更新机制,能更及时响应变化。

实际开发中哪些场景适合用query props?

别光讲理论,咱结合真实场景唠唠,你就知道这玩意儿多实用了:

列表页带筛选/分页参数

比如电商平台的商品列表,用户选了“价格区间”“分类”“排序方式”,这些筛选条件存在 URL 的 query 里(刷新页面能保留状态),用 query props 把这些参数传给列表组件,组件拿到参数后请求对应数据。

路由配置大概长这样:

{
  path: '/products',
  component: ProductList,
  props: (route) => ({
    priceRange: route.query.priceRange || '0-∞',
    category: route.query.category || 'all',
    sort: route.query.sort || 'new',
    page: Number(route.query.page) || 1
  })
}

组件里用这些 props 发请求,用户刷新页面,筛选条件还在,体验拉满。

多 Tab 切换保存状态

页面里有多个 Tab(待付款”“待发货”“已完成”),把当前激活的 Tab 标识(activeTab=waitPay)存在 query 里,用 query props 传给 Tab 组件,切换 Tab 时改 URL query,刷新页面 Tab 状态不丢。

举个简单实现:

// 路由配置
{
  path: '/order',
  component: OrderPage,
  props: (route) => ({
    activeTab: route.query.activeTab || 'waitPay'
  })
}
// 组件里,点击Tab时修改query
import { useRouter } from 'vue-router';
export default {
  props: ['activeTab'],
  setup(props) {
    const router = useRouter();
    const changeTab = (tab) => {
      router.push({ query: { activeTab: tab } }); // 改query,触发props更新
    };
    return { changeTab, props };
  }
}

搜索页传关键词

搜索页面的关键词一般存在 query 里(/search?keyword=Vue),用 query props 把 keyword 传给搜索组件,组件拿到关键词直接请求搜索接口,就算用户分享 URL,别人打开也能直接看到对应搜索结果,SEO 和体验都照顾到了。

配置query props时容易踩的坑有哪些?

前端开发哪有不踩坑的?提前避坑才能少掉头发,这些常见问题得注意:

参数类型不匹配,组件报错

query 里的参数默认都是字符串!比如你想传 page: 2,但 route.query.page 拿到的是 '2',如果组件 props 声明的是 Number 类型,直接赋值会报错(字符串转数字失败)。

解决办法:在路由的 props 函数里手动转类型。

props: (route) => ({
  page: Number(route.query.page) || 1 // 转成数字,没传就给默认值1
})

默认值没处理,props 变成 undefined

query 里没传某个参数,route.query.xxx 会是 undefined,要是组件 props 没给默认值,直接用的时候就会报错(props.xxx.length 会崩)。

解决办法:在路由 props 函数里给默认值,或者组件 props 里声明 default,推荐在路由层处理,因为路由更清楚“这个参数不传时该用啥默认值”。

同路由组件复用,props 不更新

Vue Router 有个机制:如果路由路径不变,只是 query 变了,组件会被复用(不会销毁重建),这时候 props 默认不会自动更新,组件里可能还拿着旧数据。

解决办法有两种:

  • watch 监听 props 变化,watch(() => props.page, (newVal) => { /* 发请求 */ })
  • 给组件加 key,用 query 参数当 key,强制组件重建:<component :key="$route.fullPath" />(但性能略差,谨慎用)。

和params比,query props在传参上有啥不同?

很多同学分不清 queryparams,这里简单对比下:

维度 query 参数 params 参数
存储位置 URL 中 后面(如 /page?name=xx URL 路径里(如 /user/:id/user/123
刷新保留 刷新页面,参数还在 刷新页面,参数也在(只要路由配置对)
传参场景 筛选条件、页面状态(非核心标识) 资源唯一标识(如用户ID、商品ID)
props 映射 props: true 或函数映射 query 也能 props: true 映射 params(路由配置要写 /:id

简单说,params 更像“资源的身份证”,query 更像“资源的筛选器”,比如做用户详情页,用 params/user/123);做用户列表筛选,用 query/users?gender=male&page=1)。

怎么给query props加验证?

组件的 props 本身支持类型验证、必填验证、自定义验证,这步不能省!不然传错参数很难查,举个规范的例子:

export default {
  props: {
    // 分页页码,必须是数字且大于0
    page: {
      type: Number,
      required: true,
      validator: (value) => {
        return value > 0;
      }
    },
    // 分类,可选,默认值在路由层处理过,这里兜底
    category: {
      type: String,
      default: 'all'
    }
  }
}

这样配置后,要是路由传参不符合要求(page 传了 0 或者字符串),Vue 会在控制台报错,帮你快速定位问题。

在组合式API(Composition API)里咋用query props?

<script setup> 或者 setup() 语法时,接收 props 更简单了,用 defineProps 就行:

<template>
  <div>当前页码:{{ currentPage }}</div>
</template>
<script setup>
import { defineProps } from 'vue';
// 声明props
const props = defineProps(['currentPage', 'category']);
// 直接用props.currentPage
console.log(props.currentPage);
</script>

路由配置和之前一样,用 props 函数处理 query 传给组件,要是想在 setup 里监听 props 变化,用 watch

<script setup>
import { defineProps, watch } from 'vue';
const props = defineProps(['currentPage']);
watch(
  () => props.currentPage,
  (newPage) => {
    console.log('页码变了,发请求:', newPage);
  },
  { immediate: true } // 初始化时执行一次
);
</script>

总结一下

Vue Router 的 query props 本质是把路由的查询参数,通过组件 props 传递,让组件和路由解耦,从配置方式(布尔值、函数)到实际场景(列表筛选、Tab 状态、搜索关键词),再到避坑技巧(类型转换、默认值、组件复用),掌握这些点后,写路由传参的代码会更规范、更好维护。

记住核心逻辑:路由负责处理 URL 里的 query,组件负责用 props 接收干净的数据,把这层关系理清楚,不管是现在的项目迭代,还是以后换路由方案,组件都能稳如老狗~

要是你还有其他关于 Vue Router 或者前端的问题,评论区喊我,咱接着唠!

版权声明

本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。

发表评论:

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

热门