先搞懂query params和props在Vue Router里的角色
不少用Vue开发的同学在处理路由传参时,会纠结“怎么把URL里的query参数直接当成组件props用”,毕竟直接在组件里用this.$route.query
,总觉得和路由耦合太紧,组件复用、测试都麻烦,今天就唠唠Vue Router里把query params转成props的门道,从为啥要这么做、咋配置到踩坑解决,一次讲清楚~
简单说,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; } } ]
这样UserDetail
和UserList
组件,只需要声明对应的props(id
/tab
或page
/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前端网发表,如需转载,请注明页面地址。
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。