Vue Router 核心用法与常见问题详解

Vue Router 是 Vue.js 官方的路由管理器,它和 Vue.js 的核心深度集成,让构建单页面应用变得易如反掌。本文将详细介绍 Vue Router 的核心概念、使用技巧以及常见问题的解决方案。

1. params 路由传参

params:路径隐式参数(动态路由参数),参数不拼接在 URL 地址栏,靠路由规则占位接收。

1.1 备注 1:to 对象写法传 params,只能用 name,不能写 path

正确写法(name + params,依靠路由命名匹配路由)

// ✅ 用 name 匹配路由规则,params 正常传递
router.push({
  name: 'Detail', // 路由配置里的 name 字段
  params: { id: 100 }
})

错误写法(path + params,params 失效、收不到参数)

// ❌ path 搭配 params,参数丢了,目标页面拿不到 params
router.push({
  path: '/detail',
  params: { id: 100 }
})

原理:path 是硬编码地址,无法绑定 params 参数;name 会去路由表里匹配规则,自动匹配动态占位符来装载 params。

1.2 备注 2:params 必须提前在路由规则 path 里占位

路由配置(必须提前用 :变量名 占位)

const routes = [
  {
    name: 'Detail', // 和 push 里 name 对应
    path: '/detail/:id', // :id 就是 params 的占位符
    component: Detail
  }
]
  • :id:动态占位,将来 params:{id:100} 会填充这里,最终浏览器地址变成 /detail/100

目标页面接收参数

<script setup>
import { useRoute } from 'vue-router'
const route = useRoute()
console.log(route.params.id) // 100
</script>

1.3 补充:params vs query 关键区分

params vs query 区别

2. replace 无历史记录

浏览器自带历史记录栈,前进后退按钮依赖这个栈,push/replace 就是修改栈的两种方式。

2.1 router.push()【默认跳转,追加历史】

新页面地址追加进历史栈,保留上一页记录,可以点返回按钮回到上一页。

历史栈变化示例

[首页] → push 跳转详情 → [首页,详情]

2.2 router.replace()【替换当前历史,无返回】

新地址直接覆盖替换栈顶当前页面,不会新增历史记录,不能返回上一页。

历史栈变化示例

[首页] → replace 跳转详情 → [详情]

在详情页点浏览器 ←,无法回到首页,直接跳出当前项目页面。

适用场景:登录页、支付页、404 页,不允许用户返回上一页重复操作。

示例

前置:路由配置不变 router/index.js

const routes = [
  { path: '/', name: 'Home', component: () => import('@/views/Home.vue') },
  { path: '/detail/:id', name: 'Detail', component: () => import('@/views/Detail.vue') }
]

Home.vue(首页,跳转入口页面)

<template>
  <div>
    <h3>首页页面</h3>
    <!-- 1. 默认 push:追加历史记录 -->
    <router-link :to="{ name: 'Detail', params: { id: 101 } }">
      【push 跳转详情】
    </router-link>
    <br/><br/>
    <!-- 2. 加 replace 属性:替换当前历史 -->
    <router-link replace :to="{ name: 'Detail', params: { id: 102 } }">
      【replace 跳转详情】
    </router-link>
  </div>
</template>

3. $route 和 $router 核心区别

$route 和 $router 区别

3.1 $route(拿参数、读地址)

只用来获取当前页面的URL、参数,不能跳转页面。

选项式 API 写法

<!-- template里直接用 -->
<div>
  <!-- 获取params参数 -->
  {{ $route.params.id }}
  <!-- 获取query参数 -->
  {{ $route.query.name }}
  <!-- 获取当前路径 -->
  {{ $route.path }}
</div>

组合式 API 写法

import { useRoute } from 'vue-router'
const route = useRoute()

// 获取参数
console.log(route.params.id)    // params参数
console.log(route.query.name)   // query参数
console.log(route.path)         // 当前路径

3.2 $router(控制页面跳转)

全局路由实例,用来编程跳转、前进后退

选项式 API 写法

<template>
  <button @click="goDetail">跳转</button>
</template>

<script>
export default {
  methods: {
    goDetail() {
      // push追加历史跳转
      this.$router.push({ name: 'Detail', params: { id: 10 } })
      
      // replace替换跳转
      // this.$router.replace('/home')
      
      // 返回上一页
      // this.$router.go(-1)
    }
  }
}
</script>

组合式 API 写法

import { useRouter } from 'vue-router'
const router = useRouter()

// 跳转页面
router.push({ name: 'Detail', params: { id: 10 } })
router.replace('/home')
router.go(-1) // 返回上一页

3.3 一句话总结

  • $route:查信息(取值) - 获取当前路由信息
  • $router:改地址(跳转) - 控制路由跳转

3.4 配套对应(组合式 ↔ 选项式)

useRoute()  ↔ this.$route
useRouter() ↔ this.$router

4. 编程式路由 router.push() 详解

<router-link to="xxx"> 标签 to 能写的格式,router.push(xxx) JS 里全能照搬,写法完全互通。

4.1 写法 1:纯字符串路径(搭配 query 传参,? 拼接参数)

// 等价 <router-link to="/detail?id=10">
router.push('/detail?id=10')

// 带多级路径
router.push('/home/detail')

参数在 URL:/detail?id=10,目标页面用 $route.query.id 取值。

4.2 写法 2:对象 path+query(query 用,不能配 params)

// 等价 <router-link :to="{path:'/detail',query:{id:20}}">
router.push({
  path: '/detail',
  query: { id: 20, name: 'IBM' }
})
// 地址:/detail?id=20&name=IBM

注意:path + params 会失效!params 必须用 name。

原理:path 是写死的完整路径字符串(/detail),VueRouter 拿到固定路径后,不会去路由配置表里匹配规则里的 :id 占位符,params 参数找不到可以填充的位置,直接被丢弃。

4.3 写法 3:对象 name+params

// 等价 <router-link :to="{name:'Detail',params:{id:30}}">
router.push({
  name: 'Detail', // 路由配置里的 name 字段
  params: { id: 30 }
})
// 路由 path: '/detail/:id' → 最终地址 /detail/30

重点:params 传参只能 name,禁止 path。

4.4 写法 4:name+params+query混用(两种参数同时携带)

router.push({
  name: 'Detail',
  params: { id: 40 },
  query: { type: 'goods' }
})
// 地址:/detail/40?type=goods

4.5 配套 replace 写法

replace 和 push 格式一模一样,仅替换历史记录:

// 所有 push 的参数原样丢进 replace 即可
router.replace({ name: 'Detail', params: { id: 50 } })

5. 重定向 (redirect) VS 转发 (forward)

5.1 转发 forward(一次请求,地址栏不变)

  • 用户浏览器发 1 次请求访问 /xxx
  • 服务/路由内部悄悄从 xxx.vueyyy.vue,服务器内部跳转
  • 最终页面渲染 yyy 内容,但浏览器 URL 还是原来的 /xxx,地址栏不改动
  • 只产生 1 次 HTTP 请求
  • Vue 里没有原生 forward,后端 Java/Spring 常用,特点:地址不变、一次请求

5.2 重定向 redirect(两次请求,地址栏变新地址)

  • 浏览器请求 /xxx
  • 后端返回响应,告诉浏览器:你去访问 /yyy
  • 浏览器收到指令,自动发起第二次全新请求访问 /yyy
  • 地址栏从 /xxx 变成 /yyy(URL 发生变化)
  • 一共 2 次网络请求

5.3 代码实战

路由配置重定向(路由规则里写 redirect):

const routes = [
  // 访问 / 自动重定向到 /home,地址栏变成 /home
  { path: '/', redirect: '/home' },
  { path: '/home', component: Home }
]

编程式重定向(和 replace 效果一致):

// 编程式重定向
router.replace('/home')

补充:forward 转发是后端服务端技术(Java Servlet、Spring):在服务器内部资源互换,不经过浏览器、不改 URL、一次请求。Vue 是前端 SPA 单页应用,页面都在浏览器,没有后端服务的容器环境,原生路由不支持服务端转发。

6. 如何解决重复提交问题

6.1 什么是重复提交问题

重复提交是指用户或系统不小心重复触发了同一个操作,可能会导致结果不正确或者数据重复。

常见场景:

  • 重复扣款
  • 重复下单
  • 数据库中插入多条重复记录

6.2 什么是幂等性(Idempotency)

幂等性是一个操作被执行一次或执行多次,结果完全一样。

示例:

  • 查询账户余额:你点一次还是点 100 次,结果都是当前余额,不会增加或减少。
  • 修改用户名:你把用户名改为 “Alice”,多次执行操作,结果都是 “Alice”,不会重复增加用户名。

6.3 前端 Vue3 如何防止重复提交

方法一:按钮禁用
<template>
  <button :disabled="loading" @click="submit">提交</button>
</template>

<script setup>
import { ref } from 'vue'

const loading = ref(false)

function submit() {
  if (loading.value) return

  // ========== 上锁在这里 ==========
  loading.value = true

  // 模拟请求
  setTimeout(() => {
    console.log('请求完成')
    
    // ========== 解锁在这里 ==========
    loading.value = false
  }, 2000)
}
</script>

原理

  • :disabled:禁用表单控件(按钮、输入框、下拉)
  • 静态(无 :):值写死字符串,变量改动页面不更新
  • 动态(:v-bind):绑定响应式数据,变量变化,标签属性自动变化

效果:用户点击一次,按钮禁用,防止重复提交。

方法二:前端去重 / 防抖

节流:1 秒内只放行第一次点击,剩下直接作废

// 全局变量:记录上一次成功提交的时间,初始=0
let lastTime = 0

function submit() {
  // now = 当前点击的毫秒时间戳(13位数字)
  const now = Date.now()

  // 【核心判断】现在时间 - 上次提交时间 < 1000毫秒(1秒) → 直接return结束函数
  if (now - lastTime < 1000) return  

  // 能走到这里,说明间隔超过1秒,允许提交,更新上次提交时间为当前时间
  lastTime = now
  
  // 执行提交逻辑
  console.log('执行提交')
}

防抖

let timer = null // 存计时器钥匙,初始空

function submit() {
  clearTimeout(timer) // 1. 来了新点击 → 废掉上一轮倒计时
  timer = setTimeout(() => { // 2. 新建1秒倒计时
    console.log('发请求')
  }, 1000)
}

效果:用时间限制重复点击。

方法三:后端接口幂等

前端可能防不全,有些情况还是后端要保证幂等。

常用手段:

  1. 幂等 ID:每次请求生成唯一 ID,后端记录处理过的请求
  2. 唯一约束:数据库字段唯一索引
  3. 状态检查:判断操作是否已经完成

7. 总结

Vue Router 作为 Vue.js 官方路由解决方案,提供了丰富的功能和灵活的配置选项。本文涵盖了:

  1. params 传参:注意只能用 name 不能 path,且需提前在路由规则中占位
  2. push vs replace:push 追加历史,replace 替换历史
  3. $route vs $router:前者获取信息,后者控制跳转
  4. 编程式导航:多种写法灵活应对不同场景
  5. 重定向与转发:理解两者的本质区别
  6. 重复提交防护:前端按钮禁用、节流防抖,后端幂等设计

掌握这些核心概念和技巧,能够帮助开发者更好地使用 Vue Router 构建高效、稳定的单页面应用。

更多推荐