Vue Router 怎么处理多个参数?从配置到传参再到页面使用全解析
在Vue项目开发中,经常会碰到需要给路由传递多个参数的情况,像电商详情页要同时传商品ID和分类ID、后台管理列表要带搜索关键词和分页信息这些场景,很多刚接触Vue Router的小伙伴会犯愁:“Vue Router 怎么处理多个参数?” 这篇文章从路由配置、传参方法、参数获取,到实战场景和避坑技巧,把多个参数的玩法拆解得明明白白,新手也能跟着一步步搞懂~
路由里多个参数的配置逻辑
要让路由能接收多个参数,得先搞清楚两种常见配置思路:动态路由参数(动态段)和查询参数(query)。
动态路由参数配置
动态段是路由 path
里用 开头的占位符,用来匹配 URL 里的动态部分,比如做商品详情页,需要同时传商品 ID 和分类 ID,路由可以这么配:
const routes = [ { path: '/product/:productId/:categoryId', name: 'ProductDetail', component: ProductDetail } ]
这里 productId
和 categoryId
是动态段,当用户访问 /product/123/456
时,Vue Router 会把 123
和 456
分别对应到 productId
和 categoryId
,存到 $route.params
里。
这种配置的特点是 参数是 URL 路径的一部分:用户分享链接时参数不会丢,且这些参数是访问页面的“必要条件”——少一个参数,页面可能无法正常渲染(比如商品 ID 丢了,就查不到对应商品数据)。
查询参数配置
查询参数不用改路由的 path
,而是通过 后面的键值对传递,适合传“可选参数”,比如后台订单列表的搜索页,需要关键词、分页、状态等筛选条件,路由可以这么配:
const routes = [ { path: '/admin/order', name: 'OrderList', component: OrderList } ]
访问时 URL 是 /admin/order?keyword=手机&page=2&status=待发货
,这些 keyword
、page
、status
就是查询参数,存到 $route.query
里。
查询参数的优势是 灵活可选:页面能根据有无参数做不同逻辑,刷新后参数也会保留,但它们不是 URL 路径的“必须部分”——就算不传,页面也能打开(比如默认展示第一页、所有状态的订单)。
动态段适合传“必要且固定在路径里”的参数,查询参数适合传“可选且拼接在路径后”的参数,实际项目里常结合两种方式:比如商品详情页用动态段传商品 ID 和分类 ID,再用查询参数传“是否显示推荐”这类可选配置。
给路由传多个参数的不同方式
知道了怎么配置路由,接下来得搞清楚怎么往路由里传多个参数,Vue Router 提供了好几种传参方式,不同场景选不同方式更顺手~
动态路由传参(配合动态段)
如果路由里配了动态段(比如前面的 /product/:productId/:categoryId
),传参时要把参数拼到 URL 里,有两种常用写法:
-
<router-link>
组件传参:用模板字符串把参数拼起来<router-link :to="`/product/${productId}/${categoryId}`"> 查看商品详情 </router-link>
这里
productId
和categoryId
是组件里的变量,点击后 URL 会变成/product/实际ID/实际分类ID
,参数会被解析到$route.params
里。 -
编程式导航传参:用
this.$router.push
拼接路径this.$router.push(`/product/${this.productId}/${this.categoryId}`)
这种方式和
<router-link>
原理一样,适合在点击按钮、接口回调等逻辑里触发路由跳转。
动态路由传参的特点是 参数强制可见——必须把参数拼到 URL 里才能访问对应页面,适合“没参数就开不了页”的场景(比如商品详情页没商品 ID 就查不到数据)。
查询参数(query)传参
查询参数传参不需要改路由的 path
,而是把参数放到 query
对象里,同样分 <router-link>
和编程式导航两种写法:
-
<router-link>
传 query:通过to
的对象语法传<router-link :to="{ path: '/search', query: { keyword: searchKeyword, page: currentPage } }" > 搜索 </router-link>
这里
searchKeyword
和currentPage
是组件内的变量,点击后 URL 会变成/search?keyword=xxx&page=xx
,参数存在$route.query
里。 -
编程式导航传 query:用
push
的对象语法this.$router.push({ path: '/search', query: { keyword: this.searchKeyword, page: this.currentPage } })
查询参数的优势是 灵活可缺省:就算不传某个参数,页面也能正常打开(比如默认 page=1
),而且多个参数可以自由组合,适合筛选、分页这类“多条件可选”的场景。
命名路由结合 params 传参
如果路由配了 name
(name: 'ProductDetail'
),还可以用 params
对象传参,但要注意:用 params
传参时,必须配合 name
,不能用 path
(因为 path
和 params
一起用会被 Vue Router 忽略),写法如下:
-
<router-link>
传 params:<router-link :to="{ name: 'ProductDetail', params: { productId: productId, categoryId: categoryId } }" > 查看商品详情 </router-link>
-
编程式导航传 params:
this.$router.push({ name: 'ProductDetail', params: { productId: this.productId, categoryId: this.categoryId } })
这种方式的参数会不会显示在 URL 里?要看路由有没有配动态段:
- 如果路由
path
是/product/:productId/:categoryId
,params
里的参数会被解析到 URL 路径中; - 如果路由
path
是/product
(没配动态段),params
里的参数不会显示在 URL 里,且刷新页面后参数会丢失。
所以这种方式更适合“参数必须在 URL 里体现,且路由已经配好动态段”的场景,相当于动态路由传参的“命名路由版”。
页面中怎么获取多个路由参数
路由把参数传过来了,页面组件里怎么拿到这些参数?关键是要熟悉 $route
对象里的 params
和 query
~
动态路由参数:$route.params
动态路由的参数存在 this.$route.params
里,直接取对应的键就行,比如在 ProductDetail
组件里:
<template> <div> <h1>当前商品ID:{{ productId }}</h1> <h2>所属分类ID:{{ categoryId }}</h2> </div> </template> <script> export default { data() { return { productId: '', categoryId: '' } }, created() { // 组件创建时,从 $route.params 拿参数 this.productId = this.$route.params.productId this.categoryId = this.$route.params.categoryId // 拿到参数后请求商品数据 this.fetchProduct() }, methods: { fetchProduct() { // 用 productId 和 categoryId 发请求, // axios.get(`/api/product/${this.productId}?category=${this.categoryId}`) } } } </script>
但这里有个坑:如果路由参数变化了,但组件没被销毁(比如从 /product/1/2
跳到 /product/3/2
,组件还是 ProductDetail
),created
钩子不会再执行,这时候参数更新了,页面数据却没变化,怎么解决?
-
用
watch
监听$route
的变化:watch: { '$route'(to) { // to 是新的路由对象,from 是旧的(可省略) this.productId = to.params.productId this.categoryId = to.params.categoryId this.fetchProduct() // 重新请求数据 } }
-
或者用 Vue Router 提供的
beforeRouteUpdate
导航守卫:beforeRouteUpdate(to, from, next) { this.productId = to.params.productId this.categoryId = to.params.categoryId this.fetchProduct() next() // 必须调用 next() 才能继续导航 }
查询参数:$route.query
查询参数存在 this.$route.query
里,获取方式和 params
类似,比如在 SearchPage
组件里处理搜索关键词和分页:
<template> <div> <input v-model="keyword" placeholder="请输入关键词" /> <button @click="handleSearch">搜索</button> <div>当前页码:{{ page }}</div> </div> </template> <script> export default { data() { return { keyword: '', page: 1 } }, created() { // 从 query 里拿初始参数(比如页面刷新后,保留之前的搜索条件) this.keyword = this.$route.query.keyword || '' this.page = this.$route.query.page || 1 }, watch: { '$route'(to) { // 路由变化时(比如用户点击分页按钮,路由 query 变化) this.keyword = to.query.keyword || '' this.page = to.query.page || 1 this.fetchSearchResult() } }, methods: { handleSearch() { this.$router.push({ path: '/search', query: { keyword: this.keyword, page: this.page } }) }, fetchSearchResult() { // 根据 keyword 和 page 发请求, // axios.get(`/api/search?keyword=${this.keyword}&page=${this.page}`) } } } </script>
查询参数同样要注意“组件复用导致数据不更新”的问题,所以也要用 watch
或导航守卫来处理参数变化。
用计算属性或侦听器简化代码
如果组件里要频繁用 $route.params
或 $route.query
,可以用计算属性把参数“封装”一下,让模板更简洁。
<template> <div> <h1>商品ID:{{ productId }}</h1> <h2>分类ID:{{ categoryId }}</h2> </div> </template> <script> export default { computed: { productId() { return this.$route.params.productId }, categoryId() { return this.$route.params.categoryId } }, created() { this.fetchProduct() }, watch: { productId() { this.fetchProduct() }, categoryId() { this.fetchProduct() } }, methods: { fetchProduct() { // 发请求逻辑 } } } </script>
这样模板里直接用 productId
和 categoryId
,不用每次都写 this.$route.params.xxx
,代码更干净~
多参数路由的实战场景举例
光讲理论不够直观,结合实际项目场景看看多参数路由怎么用~
电商商品详情页(动态路由多参数)
需求:用户点商品卡片进入详情页,需要展示商品信息和所属分类下的推荐商品,这时候商品 ID 和分类 ID 都是必要参数,必须放在 URL 路径里,所以用动态路由。
-
路由配置:
{ path: '/product/:productId/:categoryId', name: 'ProductDetail', component: ProductDetail }
-
传参(以编程式导航为例):
用户点商品卡片时,触发方法:handleProductClick(productId, categoryId) { this.$router.push(`/product/${productId}/${categoryId}`) }
-
组件内获取参数并请求数据:
<template> <div> <div v-if="loading">加载中...</div> <div v-else> <h1>{{ productInfo.title }}</h1> <p>{{ productInfo.desc }}</p> <h2>同分类推荐</h2> <ul> <li v-for="item in relatedProducts" :key="item.id">{{ item.title }}</li> </ul> </div> </div> </template> <script> import axios from 'axios' export default { data() { return { loading: true, productInfo: {}, relatedProducts: [] } }, created() { this.fetchData() }, watch: { '$route'() { this.fetchData() } }, methods: { async fetchData() { this.loading = true const { productId, categoryId } = this.$route.params // 请求商品详情 const productRes = await axios.get(`/api/product/${productId}`) // 请求同分类推荐商品 const relatedRes = await axios.get(`/api/category/${categoryId}/products`) this.productInfo = productRes.data this.relatedProducts = relatedRes.data this.loading = false } } } </script>
这里用动态路由把必要参数放 URL 里,既保证了页面分享的完整性,又让 SEO 更友好(搜索引擎能抓取到不同商品页的 URL)。
后台管理系统的订单列表(查询参数多参数)
需求:订单列表需要支持关键词搜索、分页、状态筛选,而且刷新页面后筛选条件要保留,这时候筛选条件是可选的,适合用查询参数。
-
路由配置:
{ path: '/admin/order', name: 'OrderList', component: OrderList }
-
传参(以
<router-link>
为例,假设当前页是第 2 页,搜索关键词是“手机”,状态是“待发货”):<router-link :to="{ path: '/admin/order', query: { keyword: '手机', page: 2, status: '待发货' } }" > 查看筛选后的订单 </router-link>
-
组件内获取参数并请求数据:
<template> <div> <input v-model="keyword" placeholder="搜索关键词" /> <select v-model="status"> <option value="">全部状态</option> <option value="待支付">待支付</option> <option value="待发货">待发货</option> </select> <button @click="handleFilter">筛选</button> <div class="pagination"> <button @click="handlePageChange(1)">第1页</button> <button @click="handlePageChange(2)">第2页</button> </div> <table> <tr v-for="order in orderList" :key="order.id"> <td>{{ order.id }}</td> <td>{{ order.name }}</td> <td>{{ order.status }}</td> </tr> </table> </div> </template> <script> import axios from 'axios' export default { data() { return { keyword: '', status: '', page: 1, orderList: [] } }, created() { // 从 query 里拿初始参数 this.keyword = this.$route.query.keyword || '' this.status = this.$route.query.status || '' this.page = this.$route.query.page || 1 this.fetchOrders() }, watch: { '$route'() { // 路由变化时(比如用户点分页按钮,query 变化) this.keyword = this.$route.query.keyword || '' this.status = this.$route.query.status || '' this.page = this.$route.query.page || 1 this.fetchOrders() } }, methods
版权声明
本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。