Vue Router里怎么玩Query Params?常见问题一次说清
做Vue项目时,路由传参是绕不开的需求,尤其是带筛选条件、分页信息这类场景,Query Params(查询参数)特别实用,但刚接触Vue Router时,很多同学会纠结“怎么传query?怎么拿?改了不跳转咋办?”这类问题,这篇用问答形式把Vue Router和Query Params的常见问题拆明白,新手也能快速上手~
Query Params是啥?和动态路由参数有啥区别?
先搞清楚概念:Query Params是URL里「?」后面的键值对,/products?category=phone&page=2
里的 category
和 page
就是Query Params,它的特点是“可选”“不影响路由匹配”——哪怕query变了,只要路径(比如/products
)不变,路由还是匹配同一个组件。
而动态路由参数是路径里的变量,/user/:id
里的 :id
,它是路由规则的一部分,必须和路径严格对应(比如/user/123
匹配,user/abc
可能404),而且动态参数变化时,默认会销毁重建组件(除非配置 keep-alive
或处理组件复用)。
举个场景例子:
- 用动态参数:用户个人页
/user/123
,每个用户ID对应唯一路径,适合“唯一资源页”。 - 用Query Params:商品列表页
/products?sort=price
,排序方式变了但路径还是/products
,适合“同一页面的不同状态”。
怎么用Vue Router传递Query Params?
Vue Router传query分「声明式导航」(<router-link>
)和「编程式导航」(router.push/replace
)两种方式,核心是给 to
对象传 query
属性。
声明式导航(<router-link>
)
在模板里用绑定 to
的对象语法:
<router-link :to="{ path: '/products', query: { category: 'laptop', page: 1 } }" > 跳转到笔记本列表 </router-link>
点击后URL会变成 /products?category=laptop&page=1
。
编程式导航(router.push/replace
)
在逻辑里用路由实例的方法,比如用户点击按钮触发筛选:
<script setup> import { useRouter } from 'vue-router'; const router = useRouter(); // 跳转并传query const handleFilter = () => { router.push({ path: '/products', query: { category: 'phone', sort: 'sales' } }); }; </script>
如果想替换当前历史记录(比如不想让用户回退到之前的状态),把 push
换成 replace
就行。
⚠️ 注意:Query Params本质是字符串,传对象/数组要手动处理!比如想传筛选条件对象,得先 JSON.stringify
,接收时再 JSON.parse
:
// 传的时候 router.push({ query: { filters: JSON.stringify({ price: [100, 200], brand: ['huawei'] }) } }); // 接收的时候 const route = useRoute(); const filters = JSON.parse(route.query.filters);
页面里怎么接收Query Params?
接收分“初始化时读取”和“参数变化时响应”两种情况,因为Vue Router的组件可能会复用(同一路径不同query时,组件不销毁),所以要注意监听变化。
初始化时读取
用 useRoute
钩子拿到当前路由对象,直接取 query
属性:
<script setup> import { useRoute } from 'vue-router'; const route = useRoute(); // 组件挂载时读取query onMounted(() => { console.log('当前分类:', route.query.category); // 比如取?category的值 console.log('当前页码:', route.query.page); }); </script>
监听Query变化
当同一路径下query变化时(比如从 ?page=1
变到 ?page=2
),组件不会重新挂载,所以要用 watch
监听 route.query
:
<script setup> import { useRoute, watch } from 'vue-router'; const route = useRoute(); watch( () => route.query, (newQuery, oldQuery) => { console.log('query变了!新参数:', newQuery); // 比如根据新的page参数重新请求列表数据 fetchProducts(newQuery.page); }, { immediate: true } // 初始化时也执行一次 ); </script>
想改Query参数但不“跳转页面”,能实现吗?
很多同学误以为“改query必须跳转到新页面”,其实只要调用router.push/replace
,哪怕路径不变,也会触发路由更新(URL的query变化,组件内的route.query
也会更新)。
比如当前URL是 /products?page=1
,想改成 ?page=2
,直接调:
router.push({ query: { page: 2 } });
这样URL会变成 /products?page=2
,同时组件内的 route.query
也会同步更新(如果用了watch监听,就能响应)。
要是不想新增历史记录(比如筛选组件的“重置”按钮,只想替换当前状态),用 router.replace
:
router.replace({ query: { page: 1, category: 'all' } });
Query Params和路由守卫怎么配合?
路由守卫(比如全局守卫、组件内守卫)可以用来校验query合法性、处理权限,举几个常用场景:
全局守卫(beforeEach
)校验参数
比如商品列表页要求 page
必须是数字,否则跳转到第1页:
// router/index.js const router = createRouter({ ... }); router.beforeEach((to, from, next) => { if (to.path === '/products') { const page = to.query.page; // 检查page是否是有效数字 if (page && isNaN(Number(page))) { // 无效则强制设为1 next({ query: { ...to.query, page: 1 } }); } else { next(); } } else { next(); } });
组件内守卫(beforeRouteUpdate
)响应参数变化
当组件复用(同路径不同query)时,用 beforeRouteUpdate
提前处理参数:
<script> export default { // 组件内守卫,路由更新前触发(query变化时会触发) beforeRouteUpdate(to, from, next) { // 根据新的query重新请求数据 this.fetchData(to.query.category); next(); } } </script>
复杂场景咋玩?比如多条件筛选、跨页面保留参数
实际开发中,Query Params最常用的场景是“页面状态持久化”(比如筛选、分页、排序)和“跨页面传参”,分享两个典型案例:
案例1:商品列表多条件筛选(状态持久化)
需求:用户选分类、价格区间、排序方式,刷新页面后筛选条件不丢,URL能分享。
实现思路:把所有筛选条件存在Query Params里,组件初始化时从query读状态,变化时更新query。
代码简化版:
<template> <div class="filters"> <select v-model="filters.category"> <option value="all">全部</option> <option value="phone">手机</option> <option value="laptop">笔记本</option> </select> <button @click="filters.price = '0-500'">低价</button> <button @click="filters.price = '500-2000'">中价</button> <select v-model="filters.sort"> <option value="default">默认排序</option> <option value="sales">销量优先</option> <option value="price">价格从低到高</option> </select> </div> </template> <script setup> import { useRoute, useRouter, watch, reactive } from 'vue-router'; const route = useRoute(); const router = useRouter(); // 初始化筛选条件:从query里拿,没有则设默认 const filters = reactive({ category: route.query.category || 'all', price: route.query.price || '0-∞', sort: route.query.sort || 'default' }); // 监听filters变化,同步到query watch(filters, (newFilters) => { router.push({ query: newFilters }); }, { deep: true }); // 根据filters请求数据(省略具体请求逻辑) const fetchProducts = () => { // 用filters里的参数发请求 }; onMounted(fetchProducts); watch(filters, fetchProducts, { deep: true }); </script>
案例2:跨页面保留筛选参数(比如列表→详情→返回列表)
需求:从商品列表(带筛选query)进入详情页,返回列表时保留之前的筛选条件。
实现思路:进入详情页时,把列表页的query存在详情页的query里,返回时自然携带。
列表页跳转代码:
// 列表页点击商品进入详情 const goToDetail = (productId) => { const { query } = useRoute(); router.push({ path: `/detail/${productId}`, query: { ...query } // 把列表页的query全带到详情页 }); };
详情页返回代码(用router.back()
):
<template> <button @click="goBack">返回列表</button> </template> <script setup> import { useRouter } from 'vue-router'; const router = useRouter(); const goBack = () => { router.back(); // 历史记录回退,自动带之前的query }; </script>
踩坑:Query Params的类型、SEO咋处理?
实际开发还有些细节容易踩坑,集中解答:
坑1:Query参数的类型丢失(数字、布尔值变字符串)
因为URL里的Query Params本质是字符串,所以传数字、布尔值会被自动转成字符串,比如传 { page: 2 }
,URL里是 ?page=2
,但 route.query.page
拿到的是字符串 "2"
。
解决方法:接收时手动转类型:
const page = Number(route.query.page) || 1; // 转数字,默认1 const isSale = route.query.isSale === 'true'; // 转布尔值
坑2:Query Params太多导致URL过长
如果传复杂对象(比如多层筛选条件),用 JSON.stringify
会让URL特别长,甚至超过浏览器限制。
解决思路:只传关键标识,而非完整数据,比如筛选条件存在Vuex/Pinia里,Query只传筛选的ID;或者后端提供“筛选配置ID”,前端存ID到query,再根据ID拉配置。
坑3:SEO不友好(SPA页面爬取不到Query参数内容)
纯SPA(单页应用)的SEO天生弱,因为搜索引擎爬取时可能不执行JS,导致带Query的页面内容被忽略。
解决方法:
- 用服务端渲染(SSR)(比如Nuxt.js),让服务端根据Query参数渲染出完整HTML,方便爬虫抓取。
- 给Query参数起语义化的键名(
category=electronics
比c=1
友好),配合Meta标签优化。
看完这些问题,是不是对Vue Router的Query Params更有数了?总结一下核心:Query是“挂在URL问号后、不影响路由匹配、用来存页面状态”的参数,传参用`to`对象的`query`属性,接收用`useRoute().query`,变化时要监听,实际项目里多结合筛选、分页这类场景练手,很快就能熟练~要是还有啥细节没搞懂,评论区随时聊~
版权声明
本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。