Skip to content

在 Vue Router 中,queryparams 是两种最常用的传参方式。它们最直观的区别就像是 GET 请求POST 请求在地址栏表现上的差异。

1. 核心区别对照表

特性Query 传参Params 传参
表现形式URL 后面带参数:/path?id=123URL 路径的一部分:/path/123
定义方式不需要配置路由路径,直接传需要在路由配置中定义占位符(如 :id
刷新丢失不会丢失(参数在 URL 中)丢失(如果不配合动态路由匹配)
属性获取$route.query$route.params
跳转方式namepath 均可必须通过 name 跳转(Vue Router 4.x 限制)

2. 详细拆解

Query 传参(类似搜索)

query 传递的参数会显示在地址栏,适合“非敏感、可分享”的数据,比如搜索关键字、分页页码等。

  • 跳转写法
javascript
// 跳转后地址:/home?id=1&name=jack
router.push({ path: '/home', query: { id: 1, name: 'jack' } })
  • 接收参数
javascript
console.log(this.$route.query.id)

Params 传参(类似详情页)

params 通常用于传递特定的标识符。

注意(Vue Router 4.x 重要变更): > 在 Vue 3 中,如果你直接使用 params 传递内存中的对象且不在路径中定义它(即隐式传参),刷新页面后参数会消失。官方推荐使用动态路由匹配或状态管理(Pinia/Vuex)替代。

  • 路由定义
javascript
{ path: '/user/:id', name: 'User', component: User }
  • 跳转写法
javascript
// 跳转后地址:/user/123
router.push({ name: 'User', params: { id: 123 } })
  • 接收参数
javascript
console.log(this.$route.params.id)

3. History API 状态传参 (State)

除了 queryparams,Vue Router 还支持通过 history.state 传递参数。这种方式非常适合传递不希望显示在 URL 中,但又需要保留的复杂对象数据。

特点

  • 隐式传递:参数不会显示在 URL 中。
  • 刷新保留:利用浏览器 History API 特性,页面刷新后 state 依然存在
  • 类型支持:可以传递对象,不需要像 query 那样手动序列化。

使用方法

发送方:

javascript
// router.push 的第二个参数可以包含 state
router.push({
  path: '/user/profile',
  state: {
    from: 'homepage',
    userInfo: { name: 'Alice', role: 'admin' }
  }
})

接收方:

javascript
// 在组件中获取
console.log(history.state.userInfo)

注意:虽然刷新页面数据会保留,但如果是新开标签页分享链接,state 是无法获取到的。它依赖于浏览器的历史记录栈。


4. 什么时候用哪个?

  1. 使用 Query 的场景
  • 需要做“搜索结果页”的分享。
  • 多个可选的筛选条件(如 ?color=red&size=m)。
  • 普通的页面跳转。
  1. 使用 Params 的场景
  • 典型的“详情页”(如商品详情 /product/1001)。
  • 参数是路由的一部分,对 SEO 更友好。
  • 看起来更简洁、美观的 RESTful 风格。

避坑小贴士 💡

  • 如果你使用了 path 进行跳转,params 会被忽略,必须使用 name
  • 如果你的 params 没在路由 path 里配置 :id,那么它就是“临时工”,刷新页面就没了。

解决 Vue Router 4.x 刷新后 params 丢失问题

这是一个非常实用的需求。在 Vue Router 4.x 中,非路径属性的 params 确实会在刷新后丢失。

为了解决这个问题,我们可以利用 localStoragesessionStorage(推荐 sessionStorage,因为它的生命周期通常与当前标签页一致)进行数据持久化。

方案:使用 sessionStorage 守卫参数

这里我用 Vue 3 + Composition API 为例,为你展示如何封装这套逻辑。

1. 发起跳转:存储数据并跳转

在发送方,我们在跳转前先将数据存入本地。

javascript
import { useRouter } from 'vue-router'

const router = useRouter()

const goToDetail = (item) => {
  // 1. 先存入本地存储(转换为 JSON 字符串)
  sessionStorage.setItem('current_params', JSON.stringify(item))

  // 2. 跳转
  router.push({
    name: 'Detail',
    params: { id: item.id } // 即使这里传了,刷新后也会丢,所以我们靠上面那行
  })
}

2. 接收页面:读取并恢复数据

在接收方页面,我们需要在组件加载时判断 params 是否为空。如果是空的(说明刷新了),就从本地存储里拿。

vue
<template>
  <div v-if="detailData">
    <h2>详情页 - {{ detailData.name }}</h2>
    <p>ID: {{ detailData.id }}</p>
    <p>描述: {{ detailData.description }}</p>
  </div>
</template>

<script setup>
import { ref, onMounted } from 'vue'
import { useRoute } from 'vue-router'

const route = useRoute()
const detailData = ref(null)

onMounted(() => {
  // 1. 尝试从当前路由参数获取 (第一次跳转过来时有效)
  const paramsData = route.params.info

  if (paramsData) {
    // 如果存在,直接赋值并存一份到本地备用
    detailData.value =
      typeof paramsData === 'string' ? JSON.parse(paramsData) : paramsData
    sessionStorage.setItem('current_params', JSON.stringify(detailData.value))
  } else {
    // 2. 如果刷新了,从本地存储恢复
    const savedData = sessionStorage.getItem('current_params')
    if (savedData) {
      detailData.value = JSON.parse(savedData)
      console.log('数据已从本地存储恢复')
    }
  }
})
</script>

3. 进阶:更好的“干净”做法

如果你觉得每个页面都写这段逻辑太麻烦,可以使用 路由守卫 (Navigation Guards) 统一处理。

router/index.js 中拦截:

router/index.js
javascript
router.beforeEach((to, from, next) => {
  // 如果是去详情页且携带了 params
  if (to.name === 'Detail' && Object.keys(to.params).length > 0) {
    localStorage.setItem('temp_params', JSON.stringify(to.params))
  }
  next()
})

💡 核心建议

  • 使用 sessionStorage:相比 localStorage,它在你关闭浏览器标签页时会自动清除,不会留下冗余的垃圾数据。
  • 优先考虑状态管理:如果你的项目较大,建议直接使用 Pinia。把数据存在 Pinia 里,再配合 pinia-plugin-persistedstate 插件,可以实现完全自动化的持久化,代码会整洁得多。