一、先搞懂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前端网发表,如需转载,请注明页面地址。
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。