Vue Router基本使用、传参、嵌套、守卫全覆盖
作者:sugar__salt
本文基于 Vue 3 + Vue Router 4,从基本使用到路由守卫,系统梳理 vue-router 的核心知识点,适合学习和复盘。
一、vue-router 的基本使用(4+2)
1.1 四个固定步骤
在 Vue 3 项目中使用 vue-router,有一套固定的初始化流程,共四个步骤:
步骤一:下载 vue-router 模块
npm i vue-router
步骤二:在 main.js 中导入相关函数
import { createRouter, createWebHashHistory } from 'vue-router'
这里导入了两个核心函数:
createRouter:用于创建路由实例createWebHashHistory:指定路由模式为"哈希模式"(URL 中带#)
步骤三:创建路由实例
// 导入页面组件
import Find from './views/Find.vue'
import My from './views/My.vue'
import Friend from './views/Friend.vue'
// 创建路由实例,配置路由表
const router = createRouter({
// 指定路由模式为哈希模式
history: createWebHashHistory(),
// 路由表:路径与组件的映射关系
routes: [
{ path: '/find', component: Find },
{ path: '/my', component: My },
{ path: '/friend', component: Friend }
]
})路由实例的核心是 routes 数组,每一条记录都定义了一个 路径(path) 与 组件(component) 的对应关系。这正是 vue-router 的本质:路径与页面组件的映射。
步骤四:注册路由实例,让规则生效
const app = createApp(App)
app.use(router) // 注册路由
app.mount('#app')
app.use(router) 这一行非常关键 —— 它会将路由能力注入整个 Vue 应用,使所有组件中都能使用路由相关的 API。
1.2 两个核心步骤
上面的四个步骤解决了"路由怎么配",但还差两个核心动作才能真正让页面跑起来。
步骤一:创建页面组件,配置规则
每个路由对应一个 .vue 页面组件,比如 Find.vue:
<script setup></script>
<template>
<div class="find">
<p>发现音乐</p>
<p>发现音乐</p>
<p>发现音乐...</p>
</div>
</template>然后将其写入路由表 routes 中,路径 /find 就对应了 Find.vue 这个页面。
步骤二:给定路由出口
在根组件 App.vue 中放置 <router-view />:
<script setup></script> <template> <!-- 路由出口:匹配到的组件会渲染在这里 --> <router-view /> </template>
<router-view />是一个"占位符",vue-router 会将匹配到的页面组件动态渲染到这个位置。
1.3 运作原理
理解路由的核心工作机制非常重要,下图描述了整个过程:
浏览器 URL 变化
↓
与 routes 数组中的 path 逐一匹配
↓
命中? ──── 是 ──→ 将对应 component 渲染到 <router-view /> 的位置
↓
否
↓
显示空白页面(或 404 页面)简单来说:URL 变化 → 匹配路由表 → 命中则渲染组件,否则空白。 这个过程是响应式的,无需手动操作 DOM。
二、抽离和封装路由模块
2.1 为什么要封装
如果所有路由代码都写在 main.js 里,随着项目变大,main.js 会越来越臃肿,难以维护。按照模块化开发的思想,我们应该把路由相关代码单独抽取到独立模块中。
2.2 如何封装
第一步:新建 src/router/index.js,将路由代码整体迁移:
import { createRouter, createWebHashHistory } from 'vue-router'
// @ 代指 src 的绝对路径,方便导入模块
import Find from '@/views/Find.vue'
import My from '@/views/My.vue'
import Friend from '@/views/Friend.vue'
const router = createRouter({
history: createWebHashHistory(),
routes: [
{ path: '/find', component: Find },
{ path: '/my', component: My },
{ path: '/friend', component: Friend }
]
})
// 默认导出 router 实例
export default router关于
@符号:在 Vite 构建的项目中,@是src目录的路径别名。@/views/Find.vue等价于src/views/Find.vue,无论当前文件嵌套多深,@始终指向src根目录,非常实用。
第二步:在 main.js 中导入并注册:
import { createApp } from 'vue'
import App from './App.vue'
import router from './router' // 导入封装好的路由模块
const app = createApp(App)
app.use(router) // 注册
app.mount('#app')封装后,main.js 从原来的 ~20 行缩减到 ~6 行,职责清晰:只负责创建 Vue 应用并挂载。路由配置的细节全部下沉到 router/index.js 中管理。
三、声明式导航
3.1 什么是声明式导航
声明式导航就是通过点击链接实现页面跳转。在普通 HTML 中我们用 <a> 标签,在 Vue 中则使用 <router-link> 组件。
3.2 基本用法
<template>
<nav>
<router-link to="/find">发现音乐</router-link>
<router-link to="/my">我的音乐</router-link>
<router-link to="/friend">朋友</router-link>
</nav>
<router-view />
</template>3.3<router-link>的本质
<router-link> 最终会被渲染成一个 <a> 标签,to 属性会变成 href 属性。但它相比原生 <a> 标签有一个重要优势:自动携带激活状态类名,可以方便地实现高亮效果。
3.4 激活类名的区别:精准匹配 vs 模糊匹配
当导航链接处于激活状态时,vue-router 会自动为当前 <a> 标签添加两个类名:
| 类名 | 匹配规则 | 示例 |
|---|---|---|
router-link-active | 模糊匹配:URL 包含 to 的属性值即激活 | URL /find/recommend 包含 /find,所以 /find 链接也会激活 |
router-link-exact-active | 精准匹配:URL 完全等于 to 的属性值才激活 | URL /find/recommend ≠ /find,所以 /find 链接不会激活 |
关键理解:相等是一种特殊的包含关系。当 URL 恰好等于
to的值时,既是精准匹配也是模糊匹配,两个类名会同时存在。
利用这些类名,可以轻松实现激活高亮:
/* 模糊匹配高亮——适合父级导航 */
nav a.router-link-active {
background: red;
color: #fff;
}
四、声明式导航传参
页面跳转时经常需要携带参数,vue-router 提供了两种传参方式。
4.1 查询参传参(query)
传递参数——两种写法本质上等价:
<!-- 方式一:字符串拼接 -->
<router-link to="/friend?id=10086">朋友</router-link>
<!-- 方式二:对象写法(最终仍会转为字符串) -->
<router-link
:to="{
path: '/friend',
query: { id: 10086 }
}">
朋友
</router-link>注意
:to前面的冒号!对象写法必须使用v-bind绑定,否则to接收的是一个普通字符串而非对象。
接收参数——在目标组件中使用 useRoute:
<script setup>
import { useRoute } from 'vue-router'
const route = useRoute() // 获取当前激活的路由对象
</script>
<template>
<p>当前朋友的 id = {{ route.query.id }}</p>
</template>
4.2 动态路由传参(params)
动态路由传参的路径更简洁,但需要先修改路由表。
第一步:改写路由表的 path,使用 :参数名 作为占位符:
routes: [
{
name: 'Friend', // 命名路由,传对象时必须用 name
path: '/friend/:fid', // :fid 是动态参数占位符
component: Friend
}
]
第二步:传递参数:
<!-- 方式一:直接拼接在路径中 -->
<router-link to="/friend/10010">朋友</router-link>
<!-- 方式二:对象写法,必须使用命名路由 name -->
<router-link
:to="{
name: 'Friend',
params: { fid: 10010 }
}">
朋友
</router-link>易错点:动态路由传对象时,必须用
name而非path!params只能配合name使用,配合path时params会被忽略。
第三步:接收参数:
<script setup>
import { useRoute } from 'vue-router'
const route = useRoute()
</script>
<template>
<p>当前朋友 id = {{ route.params.fid }}</p>
</template>4.3 两种传参方式对比
| 维度 | 查询参(query) | 动态参(params) |
|---|---|---|
| 传参写法 | /path?key1=v1&key2=v2 | /path/具体值 |
| 取参方式 | route.query.参数名 | route.params.参数名 |
| 路由表改动 | 不需要 | 需要改 path 为 /path/:参数名 |
| 适用场景 | 多参数、可选参数、搜索筛选 | 路径优雅,适合必传参数(如 ID) |
| URL 美观度 | 参数多时比较冗长 | 路径简洁美观 |
选择建议:当参数是资源的唯一标识(如用户 ID、文章 ID)时,优先用动态路由,URL 更 RESTful;当参数是可选的过滤/搜索条件时,用查询参更灵活。
五、vue-router 的更多配置
5.1 重定向(redirect)
在实际项目中会遇到两个问题:
- 用户访问根路径
/时页面一片空白 - 控制台出现黄色警告:没有匹配到
/的路由规则
解决方案:在路由表开头配置重定向规则:
routes: [
// 访问 / 时,强制跳转到 /find
{ path: '/', redirect: '/find' },
{ path: '/find', component: Find },
// ...
]
redirect 的作用是:当匹配到 path 时,立即将 URL 替换为目标路径,触发一次新的路由匹配。
5.2 404 页面
用户可能访问不存在的路径,此时不应显示空白,而应给出友好的 404 提示。
第一步:创建 404 页面组件 404.vue:
<script setup></script>
<template>
<div class="_404">
<h4>Not Found</h4>
<p>你访问的页面去了月球</p>
<router-link to="/">返回首页</router-link>
</div>
</template>
第二步:在路由表末尾添加通配规则:
routes: [
{ path: '/', redirect: '/find' },
{ path: '/find', component: Find },
{ path: '/my', component: My },
{ path: '/friend', component: Friend },
// 放在最后!前面都不匹配时才会命中
{ path: '/:pathMatch(.*)*', component: _404 }
]
生效原理:路由匹配是从上到下依次进行的。
/:pathMatch(.*)*可以匹配任意路径,但只有前面的规则全都未命中时,才会落到底部的 404 规则。如果把 404 规则放在最前面,所有路径都会被它拦截。
5.3 路由模式:哈希 vs 历史
vue-router 支持两种路由模式:
| 模式 | 函数 | URL 示例 | 特点 |
|---|---|---|---|
| 哈希模式 | createWebHashHistory() | http://localhost/#/find | URL 带 #,纯前端实现,无需服务器配合 |
| 历史模式 | createWebHistory() | http://localhost/find | URL 干净美观,接近传统多页应用 |
import { createRouter, createWebHistory } from 'vue-router'
const router = createRouter({
history: createWebHistory(), // 切换到历史模式
routes: [ /* ... */ ]
})关键提醒:在 Vite 开发环境下,历史模式可以正常使用;但项目打包上线后,历史模式需要服务器端配合配置(如 Nginx 的
try_files),否则刷新页面会 404。哈希模式则没有这个问题,因为它本质上是利用#后面的 hash 变化,不会向服务器发送请求。
六、编程式导航
6.1 是什么
声明式导航是"点击链接跳转"(被动触发),而编程式导航是通过 JavaScript 代码主动控制页面跳转。常见场景:登录成功后跳转到首页、表单提交后跳转到结果页等。
6.2 核心 API
首先需要区分两个容易混淆的 API:
import { useRouter, useRoute } from 'vue-router'
const router = useRouter() // 路由实例(createRouter 的返回值),负责"跳转"
const route = useRoute() // 当前激活的路由对象,负责"取参"| 函数 | 返回值 | 用途 |
|---|---|---|
useRouter() | 全局路由实例 | 执行跳转:push()、replace()、go() |
useRoute() | 当前路由对象 | 读取参数:route.query、route.params |
执行跳转:
// 字符串方式
router.push('/find')
// 对象方式
router.push({ path: '/find' })6.3 编程式导航传参
与声明式导航完全对应:
查询参:
// 方式一:字符串
router.push('/friend?id=10086')
// 方式二:对象
router.push({ path: '/friend', query: { id: 10086 } })动态参:
// 方式一:字符串
router.push('/friend/10010')
// 方式二:对象(必须用 name)
router.push({ name: 'Friend', params: { id: 10010 } })避坑:编程式导航的动态参传对象同样必须用
name而非path,规则与声明式导航保持一致。
6.4 声明式 vs 编程式对比
| 维度 | 声明式导航 | 编程式导航 |
|---|---|---|
| 触发方式 | 用户点击 <router-link> | JS 代码调用 router.push() |
| 适用场景 | 导航栏、菜单、面包屑等固定链接 | 登录跳转、表单提交、条件判断后跳转 |
| 语法 | <router-link to="..."> | router.push(...) |
七、路由嵌套
7.1 什么是路由嵌套
在一个路由页面内部,还可以再进行子级路由切换,这就是路由嵌套。比如"发现音乐"页面下再分为"推荐 / 排行榜 / 歌单"三个子页面。
7.2 实现步骤
第一步:准备二级页面组件(Recommend.vue、TopList.vue、PlayList.vue),并在路由表中通过 children 配置子路由:
import Recommend from '@/views/Recommend.vue'
import TopList from '@/views/TopList.vue'
import PlayList from '@/views/PlayList.vue'
const router = createRouter({
history: createWebHistory(),
routes: [
{ path: '/', redirect: '/find' },
{
path: '/find',
component: Find,
redirect: '/find/recommend', // 进入 /find 时默认展示"推荐"
children: [
{ path: 'recommend', component: Recommend }, // 注意:子路由 path 不加 /
{ path: 'toplist', component: TopList },
{ path: 'playlist', component: PlayList }
]
},
{ path: '/my', component: My },
{ path: '/friend', component: Friend }
]
})关键细节:
children中的path不要以/开头。path: 'recommend'表示相对路径,实际访问时完整路径是/find/recommend。如果写成了path: '/recommend',就会变成根路径下的路由,脱离嵌套结构。
第二步:在一级页面中放置二级路由出口。在 Find.vue中:
<template>
<div class="find">
<nav>
<router-link to="/find/recommend">推荐</router-link>
<router-link to="/find/toplist">排行榜</router-link>
<router-link to="/find/playlist">歌单</router-link>
</nav>
<!-- 二级路由出口:子路由的组件渲染在这里 -->
<router-view />
</div>
</template>此时应用中存在两个 <router-view />:
- App.vue 中的一级出口:渲染 Find / My / Friend 等一级页面
- Find.vue 中的二级出口:渲染 Recommend / TopList / PlayList 等二级页面
每一层嵌套都有自己的出口,层级分明,互不干扰。
八、路由守卫
8.1 是什么
路由守卫的全称是路由全局前置守卫(Global Before Guards)。通俗地说:在每次页面跳转之前,对访问进行拦截判断 —— 符合条件就放行,不符合就阻止或重定向。
典型应用场景:权限控制——未登录用户不能访问某些页面。
8.2 基本语法
router.beforeEach((to, from) => {
// to:目标路由对象(要去哪里)
// from:当前路由对象(从哪里来)
// 返回 true 或 undefined → 放行
// 返回 false → 阻止导航
// 返回一个路径字符串或路由对象 → 重定向到指定页面
})8.3 实战案例:未登录拦截
需求:未登录状态下,不能访问"我的音乐"页面,其他页面正常访问。
// 模拟登录状态(实际项目中从 store 或 localStorage 获取)
const isLogin = false
router.beforeEach((to, from) => {
// 未登录 且 想去"我的音乐"页面
if (!isLogin && to.path === '/my') {
alert('请先登录')
return false // 阻止跳转
}
// 其他情况一律放行
return true
})
beforeEach是全局守卫,每次路由切换都会触发。如果项目页面很多、权限规则复杂,建议按模块拆分守卫逻辑,避免单个函数过于臃肿。
8.4 守卫的返回值总结
| 返回值 | 行为 |
|---|---|
true / undefined / 不写 | 放行,导航正常进行 |
false | 阻止,取消本次导航,URL 保持不变 |
'/path' 或 { path: '/path' } | 重定向,跳转到指定路由 |
全文总结
本文从零开始,由浅入深地梳理了 Vue Router 的核心知识体系:
- 基本使用(4+2):四个固定步骤(下载 → 导入 → 创建实例 → 注册)加两个核心动作(创建页面配置规则 + 给定出口),构成了 vue-router 的最小可用闭环。
- 模块封装:将路由代码抽离到
router/index.js,遵循模块化思想,让main.js保持简洁。 - 声明式导航:
<router-link>本质是<a>标签的增强版,自带激活类名,区分模糊匹配(router-link-active)和精准匹配(router-link-exact-active)。 - 传参方式:查询参(
query)灵活多变,动态参(params)路径优雅。动态参传对象时必须用name是关键易错点。 - 更多配置:重定向解决首页空白问题,404 通配规则要放末尾,历史模式开发好用但上线需服务器配合。
- 编程式导航:
useRouter()管跳转,useRoute()管取参,两者职责不同,不可混用。 - 路由嵌套:通过
children配置子路由,子路由path不加/,每层嵌套需要自己的<router-view />出口。 - 路由守卫:
beforeEach在每次导航前执行拦截逻辑,典型场景是登录权限校验。
核心知识点复盘
- 路由本质:路径与组件的映射关系,URL 变化时自动匹配
routes数组并渲染对应组件。 <router-view />:路由出口占位符,匹配到的组件渲染在此处。嵌套路由中每一层都需要自己的出口。@别名:Vite 项目中@指向src目录,用于简化模块导入路径。- 模糊匹配 vs 精准匹配:
router-link-active是包含关系,router-link-exact-active是相等关系。相等是包含的特例,所以精准匹配时两个类名都有。 name的作用:命名路由是动态路由传参(对象写法)的必要条件。params只能配合name,配合path会被忽略。- 重定向 vs 404:重定向放路由表开头,404 通配规则放末尾。匹配顺序自上而下,顺序错了行为就不对。
- 哈希模式 vs 历史模式:哈希模式利用
#做前端路由,无需服务端支持;历史模式 URL 干净,但上线需要服务端配置回退规则。 useRoutervsuseRoute:前者是路由实例(负责跳转),后者是当前路由对象(负责取参),一字之差,职责完全不同。- 子路由 path:
children中的path不加/前缀,写相对路径即可。 - 路由守卫返回值的三种行为:
true放行、false阻止、返回路径字符串则重定向。
到此这篇关于一文学会 Vue Router:基本使用、传参、嵌套、守卫全覆盖的文章就介绍到这了,更多相关Vue Router使用内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
