Ⅰ. 基本介绍
一、单页应用程序介绍
1. 概念
单页应用程序:SPA(Single Page Application)是指所有的功能都在一个 HTML 页面上实现。
2. 具体示例
-
单页应用网站:网易云音乐 https://music.163.com/
-
多页应用网站:京东 https://jd.com/
3. 单页应用 vs 多页应用

-
单页应用类网站:系统类网站/内部网站/文档类网站/移动端站点
-
多页应用类网站:公司官网/电商类网站
二、路由介绍
1. 路由的介绍
生活中的路由:设备和 ip 的映射关系

Vue中的路由:路径和组件的映射关系

单页面应用程序,之所以开发效率高,性能好,用户体验好,最大的原因就是:页面按需更新

比如当点击【发现音乐】和【关注】时,只是局部更新内容,对于头部是不更新的,要按需更新,首先就需要明确:访问路径和组件的对应关系!
那访问路径和页面的对应关系如何确定呢?路由
2. 如何实现路由
使用 vue-router,当修改地址栏路径时,切换显示的组件即可。
3. 什么是vue-router
vue-router 是 Vue 官方的一个路由插件,是一个第三方包:

三、组件存放目录
1. 组件分类
将 .vue 文件分为两类,仅仅是为了便于理解和管理,但二者本质无区别
-
页面组件 :配合路由切换,展示整个页面,不复用的
-
复用组件 :用于组装页面组件,可复用的

2. 存放目录
-
页面组件 -> 页面展示 -> 配合路由使用 -> 放置在
src/views目录下 -
复用组件 -> 用于组装页面组件 -> 放置在
src/components目录下
Ⅱ. 基本使用和模块封装
一、基本使用(4+2)
1. 四个固定步骤
-
下载
VueRouter模块npm install vue-router -
导入相关函数
import { createRouter, createWebHashHistory } from 'vue-router' -
创建路由实例
const router = createRouter({ // 哈希模式, 路径带 # history: createWebHashHistory(), routes: [ // 路由表规则, 即 path 与 component 的对应关系 ] }) -
注册。将路由实例注册到应用中,让规则生效
app.use(router)
当完以上四步之后,就可以看到浏览器地址栏中的路径变成了 /#/ 的形式。这表示项目的路由已经被 Vue-Router 管理了。

2. 两个核心步骤
views目录下,创建需要的页面组件,并配置路由规则
views/Find.vue文件:
<script setup></script>
<template>
<div class="find">
<p>发现音乐</p>
<p>发现音乐</p>
<p>发现音乐...</p>
</div>
</template>
<style scoped></style>views/My.vue文件:
<script setup></script>
<template>
<div class="my">
<p>我的音乐</p>
<p>我的音乐</p>
<p>我的音乐...</p>
</div>
</template>
<style scoped></style>views/Friend.vue文件:
<script setup></script>
<template>
<div class="friend">
<p>朋友</p>
<p>朋友</p>
<p>朋友...</p>
</div>
</template>
<style scoped></style>main.js文件:
// 导入两个相关函数
// createRouter(): 创建路由实例
// createWebHashHistory(): 创建哈希模式的路由, 路径带 #
import { createRouter, createWebHashHistory } from 'vue-router'
// 导入 3 个页面组件
import Find from '@/views/Find.vue'
import Friend from '@/views/Friend.vue'
import My from '@/views/My.vue'
// 创建路由实例
const router = createRouter({
history: createWebHashHistory(),
routes: [
{
path: '/find',
component: Find
},{
path: '/my',
component: My
},{
path: '/friend',
component: Friend
}
]
})
// 注册
app.use(router)- 给路由出口(即路径匹配的组件,要显示的位置)
App.vue文件:
<script setup></script>
<template>
<!-- 路由出口 -->
**<router-view />**
</template>3. 路由运作原理
当浏览器 url 改变时,匹配路由表数组中的 path 值,如果命中了 ,则把相应的 component 渲染到 <router-view /> 的位置;否则显示空白。

二、抽离封装路由模块
1. 问题
路由配置代码都写在 main.js 中合适吗?显然不合适,会让 main.js 代码变得臃肿。
2. 目标
将路由模块抽离出来。好处:利于管理和维护

3. 代码示例
新建 router/index.js 文件:
import { createRouter, createWebHashHistory } from 'vue-router'
**// 注意: 脚手架环境下 @ 代指 src 目录,可以用于快速引入组件**
import Find from '@/views/Find.vue'
import Friend from '@/views/Friend.vue'
import My from '@/views/My.vue'
const router = createRouter({
history: createWebHashHistory(),
routes: [
{
path: '/find',
component: Find
},
{
path: '/my',
component: My
},
{
path: '/friend',
component: Friend
}
]
})
**// 导出路由实例**
**export default router** 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')Ⅲ. 声明式导航与传参
一、声明式导航
1. 问题
要手动输入地址,切换页面,这样不合理吧?能否点击链接进行跳转呢?
2. 解决方案
vue-router 提供了一个全局组件 router-link,用于点击跳转,需添加 to属性指定路径 。
实际上
<router-link>组件本质是<a>标签,添加to属性本质是给<a>标签添加href属性。
语法:<router-link to="path值"> xxx </router-link>
<script setup></script>
<template>
<nav>
**<router-link to="/find">发现音乐</router-link>**
**<router-link to="/my">我的音乐</router-link>**
**<router-link to="/friend">朋友</router-link>**
</nav>
</template>
<style>
nav a {
color: #333;
text-decoration: none;
}
nav a:nth-child(2) {
margin: 0 80px;
}
**nav a.router-link-active** {
background: red;
color: #fff;
}
</style>3. 自带高亮类名
使用 <router-link> 跳转后,可以发现当前点击的链接默认加了两个 class 的值:router-link-exact-active 和 router-link-active,我们可以给任意一个 class 属性添加高亮样式即可实现功能,如上面的代码所示。

二、router-link-active 和 router-link-exact-active 的区别
它们的区别在于匹配规则:
-
router-link-active-
模糊匹配 (包含关系)。
-
只要当前路由 以该路径开头 ,就会添加这个类。
-
例子:
-
链接是
/home -
当前路由是
/home/news -
也会被标记为
router-link-active。
-
-
-
router-link-exact-active-
精确匹配 (完全一致)。
-
只有当前路由 完全等于该路径 时才会添加。
-
例子:
-
链接是
/home -
当前路由是
/home/news -
不会加
router-link-exact-active,只有当路由正好是/home时才加。
-
-
三、声明式导航传参
1. 需求
在跳转路由时如果需要进行传参,比如:现在我们在发现音乐页,点击去朋友页,并携带 id,方便后续查询详情,如何传参?
2. 传参分类
我们可以通过两种方式,在跳转的时候把所需要的参数传到其他页面中
-
查询参数传参
-
动态路由传参
3. 查询参数传参
在传参 时候,有两种格式,一种是字符串形式传参,一种是对象形式传参!
-
字符串
<router-link **to="/path?参数名=值"** > xxx </router-link> -
对象
<router-link **:to="{** **path: '/path',** **query:** { 参数名: 值 ... **}** **}"** > xxx </router-link>
在接收参数 时候,使用 useRoute() 获取 route 路由对象,通过 route.query 获取到传递过来的参数。
<script setup>
**import { useRoute } from 'vue-router'**
// 得到当前激活的路由对象
**const route = useRoute()**
// 获取查询参数
console.log(**route.query.参数名** )
</script>代码示例
App.vue文件:
<!-- 字符串 -->
<router-link to="/friend?id=10086"> 朋友 </router-link>
<!-- 对象 -->
<router-link **:** to="{
path: '/friend',
query: {
id: 10086
}
}"> 朋友 </router-link>Friend.vue文件:
<script setup>
import { useRoute } from 'vue-router'
const route = useRoute()
console.log(route.query.id)
</script>4. 动态路由传参
步骤:
-
配置动态路由
-
动态路由后面的参数可以随便起名,但要有语义

const router = createRouter({
...
routes: [
...
{
name: 'Friend',
**path: '/friend/:fid'** ,
component: Friend
}
]
})在传参 时候,同样有两种格式,一种是字符串形式传参,一种是对象形式传参!
-
字符串
<router-link to="/path/具体值"> xxx </router-link> -
对象
<router-link :to="{ **name: 'Friend'** , **params:** { 参数名: 具体值 } }"> xxx </router-link>
接收参数时候,和前面不同的是这里要用 route.params 获取传递过来的参数。
<script setup>
import { useRoute } from 'vue-router'
const route = useRoute()
// 获取动态路由参数
console.log(**route.params** )
</script>代码示例
router/index.js文件:
createRouter({
routes: [
{
name: 'Friend',
path: '/friend/:fid',
component: Friend
}
]
})App.vue文件:
<!-- 字符串 -->
<router-link to="/friend/10010"> 朋友 </router-link>
<!-- 对象 -->
<router-link :to="{
name: 'Friend',
params: {
fid: 10010
}
}"> 朋友 </router-link>Friend.vue文件:
<script setup>
import { useRoute } from 'vue-router'
const route = useRoute()
console.log(route.params.fid)
</script>Ⅳ. 更多配置
一、重定向
1. 问题
在网页打开时,url 默认是 / 路径,未匹配到组件时,会出现空白内容,如下所示:

2. 解决方案
重定向:匹配 / 后,强制跳转到其它页面,比如跳转到 /find 路径,避免页面空白。
3. 语法
在创建路由 createRouter() 时候添加对 / 的重定向路径,如下所示:
const router = createRouter({
...
routes: [
// 访问 / , 自动跳转到 /find
**{**
**path: '/', **
**redirect: '/find'**
**},**
...
]
})二、404页面
1. 作用
当路径找不到匹配时,给个提示页面.
2. 语法
① Vue Router 3(Vue2 生态)
{ path: '*', component: _404 }-
*通配符写法,只能用在最后一条路由。 -
匹配任意路径,通常用于 404。
-
缺点:没法拿到具体被匹配的路径字符串。
② Vue Router 4(Vue3 生态)
{ path: '/:pathMatch(.*)*', component: NotFound }
{ path: '/user-:afterUser(.*)', component: UserGeneric }-
用正则样式的参数写法。
-
/:pathMatch(.*)*表示 "匹配所有路径片段",结果会放在route.params.pathMatch里。 -
/user-:afterUser(.*)表示匹配/user-xxx开头的路径,并把xxx后面部分存在params.afterUser。-
:afterUser→ 这是一个动态路由参数名 ,访问时的值会放到route.params.afterUser。 -
(.*)→ 这是一个 正则表达式 ,约束参数可以匹配的内容。-
.表示任意字符 -
*表示出现 0 次或多次 -
所以
(.*)就是 "匹配任意长度的字符串"
-
-
-
优点:更灵活,可以在 404 页面显示 "你访问的路径是 xxx"。
两种语法的区别
-
path: '*'→ Vue Router 3 的老写法,简单粗暴,只能兜底。 -
/:pathMatch(.*)*→ Vue Router 4 的推荐写法,支持参数提取和更强的模式匹配。
实例代码
views/404.vue文件:
<script setup> </script>
<template>
<div>
<h3>404</h3>
<p> 你访问的页面去了月球 </p>
<router-link to="/"> 去首页 </router-link>
</div>
</template>router/index.js文件:
import _404 from '@/views/404'
const router = createRouter({
routes: [
...
{
**path: '/:pathmatch(.*)*'** ,
**component: _404**
}
]
})
export default router三、路由模式
1. 问题
路由的路径看起来不好看,开头有 #,能否切成真正路径形式?
-
hash路由 (默认),例如:http://localhost:5173/#/find,不够美观! -
history路由 (开发常用),例如:http://localhost:8080/find(**缺点是刷新或直接访问时会请求服务器** ,需要后端配置history fallback)
2. 语法
import { createRouter, createWebHashHistory, **createWebHistory** } from 'vue-router'
const router = createRouter({
// 历史模式: createWebHistory(), 路径带 #
// 哈希模式: createWebHashHistory(), 路径不带 #
// history: createWebHashHistory(),
**history: createWebHistory()** ,
...
})Ⅴ. 编程式导航与传参
一、编程式导航
1. 问题
如何主动做路由跳转?比如:登录成功自动跳转至首页
2. 解决方案
编程式导航:用 JS代码跳转路由 ,而不是像声明式导航那样写在模板里的 <router-link>。
3. 语法
通过 useRouter() 获取路由实例 router,然后由 router.push(路径) 添加路由内容。
其中该路由内容可以是字符串形式,也可以是对象形式,这和声明式导航是一样的!
<script setup>
import { useRouter } from 'vue-router'
const router = **useRouter** ()
router.push(字符串)
router.push(对象)
</script>4. 路由对象 vs 路由实例
之前 **声明式导航 ** 中使用的是 useRoute() 来创建当前的路由对象,但是 编程式导航 则不一样,使用的是 useRouter() 来创建路由实例,这两者是完全不同的,区别在于一个读数据,一个改数据:
① useRoute()
-
返回 当前路由对象 (等价于
this.$route)。 -
包含的信息:
path、params、query、fullPath、matched等。 -
只读 ,用来获取路由状态,表示 "你此刻在哪" 。
-
例子:
import { useRoute } from 'vue-router' const route = useRoute() console.log(route.query.page) // 查询参数 console.log(route.params.id) // 动态参数
② useRouter()
-
返回 路由实例 (等价于
this.$router)。 -
提供导航方法:
push(跳转到新地址)、replace(替换当前地址)等。 -
可写 ,用来跳转或修改路由,表示 "你怎么去别的地方" 。
-
例子:
import { useRouter } from 'vue-router' const router = useRouter() router.push('/home') // 跳转 router.push({ name: 'user', params: { id: 123 } })
代码示例
需求:组件挂载后,延迟2秒,自动从 发现音乐页 跳转至** 朋友页** 。
Find.vue文件:
<script setup>
import { useRouter } from 'vue-router'
import { onMounted } from 'vue'
const router = useRouter()
// 组件挂载后, 延迟2秒, 自动从 发现音乐页 跳转至 朋友页
onMounted(() => {
setTimeout(() => {
// 1、字符串形式
router.push('/friend')
// 2、对象形式:path属性
router.push({
path: '/friend'
})
// 3、对象形式:name属性
router.push({
name: 'Friend'
})
}, 2000)
})
</script>二、编程式导航传参
编程式导航与声明式导航的传参和接参方式完全一样,既支持字符串,也支持对象,如下所示:
// 1、查询参数传参:字符串形式
router.push('/path?参数名=值')
// 2、查询参数传参:对象形式
router.push({
path: '/path',
query:{
参数名: 值
...
}
})
// 3、动态路由传参:字符串形式
router.push('path/参数值')
// 4、动态路由传参:对象形式
router.push({
name: '路由名称',
params: {
参数名: 值
}
})Ⅵ. 嵌套与守卫
一、路由嵌套
1. 问题
能否在一个路由页面中,再进行一套路由的切换呢?比如网易云音乐的发现音乐页

2. 步骤
-
这里创建3个二级路由页面组件(Recommend、Ranking、SongList)来举例子
-
然后在路由表数组中,在相应的一级路由规则中,配置
children规则 -
在相应的一级路由页面组件中,给二级路由
<router-view/>出口和导航链接
💥注意事项:
-
二级路由的
path不需要在开头加/ -
做路由跳转的时候要写 完整路径 (父路径+当前路径)
-
防止二级路由页面空白,给默认显示的一级路由 添加重定向
代码示例
views/Recommend.vue文件:
<script setup></script>
<template>
<div class="recommend">
<h5>推荐</h5>
<h5>推荐</h5>
<h5>推荐</h5>
</div>
</template>
<style scoped></style>views/Ranking.vue文件:
<script setup></script>
<template>
<div class="ranking">
<h5>排行榜</h5>
<h5>排行榜</h5>
<h5>排行榜</h5>
</div>
</template>
<style scoped></style>views/SongList.vue文件:
<script setup></script>
<template>
<div class="songlist">
<h5>歌单</h5>
<h5>歌单</h5>
<h5>歌单</h5>
</div>
</template>
<style scoped></style>router/index.js文件:
import Recommend from '@/views/Recommend.vue'
import Ranking from '@/views/Ranking.vue'
import SongList from '@/views/SongList.vue'
const router = createRouter({
history: createWebHashHistory(),
routes: [{
path: '/find',
component: Find,
**redirect: '/find/recommend'** , // 重定向, 防止二级路由空白
// 配置嵌套路由
**children** : [{
// 二级路由开始, 推荐 path 不加 /
path: 'recommend',
component: Recommend
},{
path: 'ranking',
component: Ranking
},{
path: 'songlist',
component: SongList
}]
}]
})views/Find.vue文件:
<script setup></script>
<template>
<div class="find">
<nav>
<!-- 二级路由导航链接 -->
<router-link **to="/find/recommend"** >推荐</router-link>
<router-link **to="/find/ranking"** >排行榜</router-link>
<router-link **to="/find/songlist"** >歌单</router-link>
</nav>
<!-- 二级路由出口 -->
**<router-view />**
</div>
</template>
<style scoped></style>二、路由守卫
1. 问题
能否在访问某个路由前,添加权限判断?比如我的音乐页,只有登录了才可以访问。
2. 解决方案
路由(导航)全局前置守卫:每个路由在跳转前都会触发回调函数,可以通过 router.beforeEach() 为这些路由设置判断条件,以此完成权限判断等功能。
router.**beforeEach** ((to, from) => {
**// to: 即将要进入的路由**
**// from: 当前正要离开的路由**
// false 取消导航(不发生跳转)
return false
// 正常放行、正常跳转
return undefined | true
// 重定向到指定的路由
return '/路径'
})代码示例
router/index.js文件:
// 用来模拟是否登录
const isLogin = true
router.beforeEach((to, from) => {
// 如果没有登录, 并且还要去 我的音乐页
if(!isLogin && to.path === '/my') {
// 进行提示
alert('请先登录')
// 不放行(不跳转)
return false
}
// 正常放行
return true
})