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

先搞懂query params和props在Vue Router里的角色

terry 11小时前 阅读数 9 #Vue
文章标签 Vue Router;props

不少用Vue开发的同学在处理路由传参时,会纠结“怎么把URL里的query参数直接当成组件props用”,毕竟直接在组件里用this.$route.query,总觉得和路由耦合太紧,组件复用、测试都麻烦,今天就唠唠Vue Router里把query params转成props的门道,从为啥要这么做、咋配置到踩坑解决,一次讲清楚~

首先得明确两个概念: - **query params** 是URL中`?`后面跟着的参数,比如访问`/page?name=小明&age=18`,`$route.query`里就存着`{ name: '小明', age: '18' }`,它属于路由实例`$route`的属性,和当前路由状态强绑定。 - **props** 是Vue组件接收外部数据的“接口”,组件里声明`props: ['name']`后,父组件(或路由)就能通过``传值,让组件不用关心数据从哪来,只负责渲染,实现解耦。

简单说,query params是“路由里带的数据”,props是“组件要的输入”,把前者转成后者,就是让组件和路由的耦合度降低,更灵活~

为啥要把query params转成props?

很多同学一开始会直接在组件里写this.$route.query.name,能用但有隐患:

  • 组件没法复用:如果另一个地方想不用路由,直接传name给这个组件,得改代码;
  • 测试麻烦:写单元测试时,得mock整个$route对象,而传props的话,直接传值就行;
  • 逻辑混杂:组件里又要处理业务逻辑,又要关心路由结构,代码变乱。

换成props传query参数后,好处很直观:

  • 解耦路由和组件:组件只看props里的数据,不管数据是不是从路由来的;
  • 复用性拉满:同一个组件,既能用在路由场景(接收query转的props),也能在普通父子组件里用(父组件直接传props);
  • 测试简单:给props传不同值就能测组件逻辑,不用管路由那套。

具体怎么配置,让query params变成props?

Vue Router的路由配置里,props选项支持函数形式,能把route对象(包含query、params等)转成props对象,步骤很清晰:

第一步:路由配置里写props函数

router/index.js(或路由配置文件)里,给对应路由加props函数,把route.query里的参数映射到props:

const routes = [
  {
    path: '/user',
    component: UserComponent,
    // 用函数把route的query转成props
    props: (route) => ({
      name: route.query.name, 
      age: route.query.age,
      // 还能加默认值,比如gender默认值
      gender: route.query.gender || '未知'
    })
  }
]

第二步:组件里声明props

UserComponent.vue里,用props接收这些参数:

<template>
  <div>
    姓名:{{ name }},年龄:{{ age }},性别:{{ gender }}
  </div>
</template>
<script>
export default {
  props: {
    name: String,
    age: String, // 注意:query参数默认是字符串,要转类型得自己处理
    gender: String
  }
}
</script>

这样一来,访问/user?name=小红&age=20时,UserComponent的props里就有name='小红'age='20'gender='未知',不用再写this.$route.query啦~

处理query参数变化时的“响应性”问题

路由的query变化时(比如从/user?name=小红跳到/user?name=小明,路由没换,只是query变了),组件默认不会重新渲染(因为组件实例没销毁),这时候props里的值也得跟着变,不然页面不更新,咋解决?

方法1:用watch监听$route

在组件里监听$route的变化,当query变了,主动处理逻辑:

<script>
export default {
  props: ['name', 'age'],
  watch: {
    '$route'(to, from) {
      // to.query里是新的参数,能拿到变化后的值
      console.log('query变了,新name:', to.query.name)
      // 也能手动触发数据更新,比如调用方法重新请求数据
      this.fetchData(to.query.name)
    }
  },
  methods: {
    fetchData(name) {
      // 根据name请求数据...
    }
  }
}
</script>

方法2:给组件加key,强制重新渲染

在路由出口<router-view>上绑定key$route.fullPath(包含path和query的完整路径),这样query一变,key就变,组件会重新渲染:

<router-view :key="$route.fullPath"></router-view>

方法3:在props函数里做响应式处理

因为props函数返回的对象会被Vue处理成响应式,所以只要query参数变化,props也会自动更新(前提是Vue的响应式系统能检测到变化),比如路由配置里的props函数返回的是包含query参数的对象,组件里的props就能自动响应~

进阶:结合路由元信息(meta)灵活配置

如果多个路由都用同一个组件,但需要传递的query参数不一样,总不能每个路由都写一遍props函数吧?这时候路由元信息(meta) 就派上用场了~

思路是:在路由的meta里定义需要传递的query参数键名,然后用props函数统一处理:

const routes = [
  {
    path: '/user/detail',
    component: UserDetail,
    meta: { queryProps: ['id', 'tab'] }, // 定义要传的query键
    props: (route) => {
      const props = {};
      // 遍历meta里的queryProps,把对应的query值塞到props里
      route.meta.queryProps.forEach(key => {
        props[key] = route.query[key];
      });
      return props;
    }
  },
  {
    path: '/user/list',
    component: UserList,
    meta: { queryProps: ['page', 'size'] }, // 另一个路由传不同的query键
    props: (route) => {
      const props = {};
      route.meta.queryProps.forEach(key => {
        props[key] = route.query[key];
      });
      return props;
    }
  }
]

这样UserDetailUserList组件,只需要声明对应的props(id/tabpage/size),就能接收各自路由的query参数,不用重复写props逻辑,灵活度拉满~

常见坑点 & 解决办法

用query转props时,这些问题很容易碰到,提前避坑:

坑1:query参数是字符串,想要数字/布尔值

比如age应该是数字,但route.query.age拿到的是字符串"18",解决方法:在props函数里转类型:

props: (route) => ({
  age: Number(route.query.age) || 18, // 转成数字,没值时默认18
  isVip: route.query.isVip === 'true' // 转成布尔值
})

坑2:query参数不存在时,props没默认值

如果希望参数不传时用默认值,要么在props函数里判断赋值(如上面的age),要么在组件props里写default,但注意:路由的props函数返回的优先级更高,所以更推荐在函数里处理:

props: (route) => ({
  gender: route.query.gender || '未知' // 直接给默认值
})

坑3:同一路由query变化,组件不更新

前面讲过,这是因为组件实例没销毁,解决方法回顾:用watch监听$route、给<router-view>key、或者确保props函数返回的对象是响应式的(一般Vue会自动处理)。

这么做值不值?

把Vue Router的query params转成props,核心是解耦组件和路由,让组件更纯粹、更好复用,配置起来就两步:路由里用props函数映射query到props,组件里声明props接收,过程中注意参数类型转换、响应性处理,再结合meta做灵活配置,基本能覆盖大部分场景~

要是你之前直接用$route.query写了一堆代码,不妨试试这种方式重构,代码会清爽很多;要是刚学Vue Router,从一开始就这么做,后续维护和扩展会轻松不少~

版权声明

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

发表评论:

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

热门