一、先搞懂vue router里的params是干啥的?
在Vue项目开发里,经常遇到需要根据不同数据渲染页面的情况,比如商品详情页要根据商品ID展示不同内容、用户个人页要根据用户ID加载信息,这时候vue router的params参数就派上用场了!可vue router with params到底咋用?从路由配置到传参、接收,再到避坑,今天一次性讲明白~
params是Vue Router里用来传递动态路由参数的机制,和URL里看得见的query参数(name=xxx)不一样,params更像“藏在路由结构里”的参数,举个更具体的例子:你做一个博客系统,文章详情页需要根据文章ID展示内容,如果用query,URL会是/article?articleId=1,但用params+动态路由,URL是/article/1,后者不仅URL更美观,SEO友好度也更高(搜索引擎更喜欢语义化的路径),而且从产品逻辑来说,文章ID是访问文章详情的必要条件,放在路由的动态段里,也能让路由配置更严谨——如果用户直接输/article(没有ID),路由匹配不到,会跳404或者重定向,避免无效访问。
配置带params的路由规则,得这么写!
路由配置文件(比如router/index.js)是整个路由系统的“规则手册”,要让params生效,得先在规则里留好“位置”。
错误示范:如果路由配置写成这样,params根本没地方放:
const routes = [
{
path: '/user',
name: 'User',
component: User
}
]
这时就算传params: { id: 123 },URL还是/user,params也传不过去——因为路由规则里没有给params留动态段。
正确示范必须加动态段(用开头的字段):
const routes = [
{
name: 'User', // 路由命名很重要,后面传参要用到
path: '/user/:id', // :id就是给params留的位置,代表用户ID
component: User
}
]
这样一来,当访问/user/123时,123会被自动解析成params里的id值,要是需要多个参数,比如订单ID和详情ID,路由可以写成/order/:orderId/detail/:detailId,对应params里就有orderId和detailId两个字段,命名和后续传参、接收保持一致就行~
用<router-link>声明式导航传params
在页面里写跳转链接(比如用户列表里的“查看主页”按钮),用<router-link>这种声明式导航最方便,核心是给to绑定一个对象,用name匹配路由,再用params传参。
举个实际例子:用户列表页面里,每个用户项对应一个跳转链接:
<template>
<div class="user-list">
<div v-for="user in userList" :key="user.id">
<router-link
:to="{
name: 'UserDetail', // 必须和路由配置的name一致
params: { id: user.id } // 把用户ID传给详情页
}"
>
{{ user.name }}的个人主页
</router-link>
</div>
</div>
</template>
<p><script>
export default {
data() {
return {
userList: [
{ id: 1, name: '小明' },
{ id: 2, name: '小红' }
]
}
}
}
</script>
点击“小明的个人主页”,URL会变成/user/1,对应的UserDetail组件就能拿到params里的id啦~这里要注意:必须用name匹配路由,不能用path!如果改成path: '/user/:id'再传params,Vue Router会直接忽略params,传参失败。
用router.push编程式导航传params
如果是点击按钮、触发方法后跳转(查看当前用户主页”按钮),就得用编程式导航(router.push),Vue2和Vue3的写法略有不同,但核心逻辑一样:用name匹配路由,params传参。
Vue2写法(选项式API):
<template>
<button @click="goToCurrentUser">查看我的主页</button>
</template>
<p><script>
export default {
data() {
return {
currentUserId: 123 // 假设从登录态拿到用户ID
}
},
methods: {
goToCurrentUser() {
this.$router.push({
name: 'UserDetail', // 路由的name
params: { id: this.currentUserId } // 传用户ID
})
}
}
}
</script>
Vue3写法(组合式API):需要先引入useRouter:
<template>
<button @click="goToCurrentUser">查看我的主页</button>
</template>
<p><script setup>
import { useRouter } from 'vue-router'
const router = useRouter()
const currentUserId = 123 // 假设从Pinia或sessionStorage拿到
const goToCurrentUser = () => {
router.push({
name: 'UserDetail',
params: { id: currentUserId }
})
}
</script>
点击按钮后,路由会跳转到/user/123,params里的id就被成功携带啦~同样要记住:必须用name,不能用path,否则params传不过去!
组件里咋接收params?分Vue2和Vue3说
不管是Vue2还是Vue3,接收params都要从当前路由对象里拿(不是操作路由的实例哦),Vue2用$route,Vue3用useRoute。
Vue2组件接收(选项式API):
export default {
mounted() {
const userId = this.$route.params.id // 拿到路由里的id参数
this.fetchUserData(userId) // 发请求拿用户信息
},
methods: {
fetchUserData(id) {
// 模拟接口请求
axios.get(`/api/user/${id}`).then(res => {
this.userInfo = res.data
})
}
}
}
Vue3组件接收(组合式API):需要引入useRoute:
<script setup>
import { onMounted } from 'vue'
import { useRoute } from 'vue-router'
import axios from 'axios'
<p>const route = useRoute()
const userInfo = ref({})</p>
<p>onMounted(() => {
const userId = route.params.id
fetchUserInfo(userId)
})</p>
<p>const fetchUserInfo = (id) => {
axios.get(<code>/api/user/${id}</code>).then(res => {
userInfo.value = res.data
})
}
</script>
如果路由配置里有多个动态参数(比如/order/:orderId/detail/:detailId),接收时就是route.params.orderId和route.params.detailId,按需取值就行~
动态路由匹配:params和组件复用的坑
当多个不同的路由对应同一个组件时(比如/user/123和/user/456都渲染User组件),Vue为了性能会复用组件——这意味着组件的生命周期钩子(比如created、mounted)不会重新执行!举个场景:做电商APP的商品列表,点击商品A(/product/1)跳转到详情页,再点商品B(/product/2),如果不处理,详情页还是商品A的内容,因为组件没销毁,生命周期没重新走。
解决方法分Vue2和Vue3:
▎Vue2:用watch监听$route变化
export default {
data() {
return {
productInfo: {}
}
},
mounted() {
this.fetchProduct(this.$route.params.productId)
},
watch: {
// 监听params里的productId变化
'$route.params.productId'(newId) {
this.fetchProduct(newId)
}
},
methods: {
fetchProduct(id) {
// 发请求拿商品信息
}
}
}
▎Vue3:用onBeforeRouteUpdate导航守卫
<script setup>
import { onBeforeRouteUpdate, onMounted } from 'vue'
import { useRoute } from 'vue-router'
import axios from 'axios'
<p>const route = useRoute()
const productInfo = ref({})</p>
<p>const fetchProduct = (id) => {
axios.get(<code>/api/product/${id}</code>).then(res => {
productInfo.value = res.data
})
}</p>
<p>onMounted(() => {
fetchProduct(route.params.productId)
})</p>
<p>// 路由更新前执行,拿到新的productId
onBeforeRouteUpdate((to) => {
fetchProduct(to.params.productId)
})
</script>
这样不管是首次进入还是切换路由,都能拿到最新的参数,请求对应数据,页面就会正确更新~
用params传参的注意事项,避坑必看!
用params时踩过的坑不少,这几个点一定要注意:
刷新页面,params可能丢失
如果路由配置里没有动态路由(比如path是/user,不是/user/:id),只是通过name传params,刷新页面后params会直接消失!因为params本质是“基于路由名传递的内存参数”,没被编码到URL里。
解决方法:
- 把参数放到动态路由里(推荐):路由配置写成
/user/:id,这样URL里有id,刷新也能保留。 - 用sessionStorage暂存:传参时把params存到sessionStorage,组件里优先从$route.params拿,拿不到再从sessionStorage取。
// 传参时存
this.$router.push({ name: 'User', params: { id: 123 }})
sessionStorage.setItem('currentUserId', 123)
<p>// 组件里取
mounted() {
const userId = this.$route.params.id || sessionStorage.getItem('currentUserId')
this.fetchUserData(userId)
}
但要注意sessionStorage的清除逻辑,避免数据混乱~
传参时,name和path别搞混
很多新手会犯的错:路由配置有name,传参时却用path。
this.$router.push({
path: '/user', // 错误!用path的话params传不过去
params: { id: 123 }
})
这时URL还是/user,params根本没传过去。params是和路由的name绑定的,必须用name传参!
params和query能一起用吗?
完全可以!比如做带筛选的用户列表,用户ID是必须的(params),筛选条件是可选的(query),路由配置:
const routes = [
{
name: 'UserFilter',
path: '/user/:id/filter',
component: UserFilter
}
]
传参时:
this.$router.push({
name: 'UserFilter',
params: { id: 123 },
query: { gender: 'male', age: 20 }
})
这时URL是/user/123/filter?gender=male&age=20,params里有id,query里有筛选条件,组件里分别拿$route.params.id和$route.query.gender就行~
路由参数的可选匹配
如果想让参数可选(比如用户主页可以带ID,也可以不带),路由配置加问号:
const routes = [
{
name: 'User',
path: '/user/:id?', // ?表示id可选
component: User
}
]
访问/user时,params.id是undefined;访问/user/123时,params.id是123,组件里要处理id为undefined的情况,比如展示默认页面~
就是vue router with params的核心用法和避坑指南啦!从路由配置、传参到接收,再到处理组件复用和参数丢失,把这些点吃透,开发中遇到动态参数需求就不会慌啦~要是还有疑问,评论区随时喊我~
版权声明
本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。
code前端网


