第10章****Vue Router
目录
[10.1 什么是路由](#10.1 什么是路由)
[10.2 Vue Router的安装](#10.2 Vue Router的安装)
[10.2.1 本地独立版本方法](#10.2.1 本地独立版本方法)
[10.2.2 CDN方法](#10.2.2 CDN方法)
[10.2.3 NPM方法](#10.2.3 NPM方法)
[10.2.4 命令行工具(Vue CLI)方法](#10.2.4 命令行工具(Vue CLI)方法)
[10.3 Vue Router的基本用法](#10.3 Vue Router的基本用法)
[10.3.1 跳转与传参](#10.3.1 跳转与传参)
[10.3.2 配置路由](#10.3.2 配置路由)
[10.4 Vue Router高级应用](#10.4 Vue Router高级应用)
[10.4.1 动态路由匹配](#10.4.1 动态路由匹配)
[10.4.2 嵌套路由](#10.4.2 嵌套路由)
[10.4.3 编程式导航](#10.4.3 编程式导航)
[10.4.4 命名路由](#10.4.4 命名路由)
[10.4.5 重定向编辑](#10.4.5 重定向编辑)
[10.4.6 路由组件props传参](#10.4.6 路由组件props传参)
[10.4.7 HTML5 历史记录模式](#10.4.7 HTML5 历史记录模式)
[10.5 路由钩子函数](#10.5 路由钩子函数)
[10.5.1 全局前置钩子函数](#10.5.1 全局前置钩子函数)
[10.5.2 全局解析钩子函数](#10.5.2 全局解析钩子函数)
[10.5.3 全局后置钩子函数](#10.5.3 全局后置钩子函数)
[10.5.4 某个路由的钩子函数](#10.5.4 某个路由的钩子函数)
[10.5.5 组件内的钩子函数](#10.5.5 组件内的钩子函数)
[10.6 路由元信息](#10.6 路由元信息)
10.1 什么是路由
路由,本是一个网络工程术语,是指分组从源到目的地时,决定端到端路径的网络范围的进程。在Web前端单页面应用中,路由描述的是URL与UI之间的映射关系,这种映射是单向的,即URL变化引起UI更新(无需刷新页面)。
Vue Router 是 Vue.js 官方的路由管理器,它和 Vue.js 的核心深度集成,使构建单页面应用变得更加容易。
10.1.1 SPA与前端路由
SPA指的是一个web网站只有唯一的一个HTML页面,所有组件的展示与切换都在唯一的一个页面内完成。
此时,不同组件之间的切换需要通过前端路由
来实现。
结论:在 SPA 项目中,不同功能之间的切换,要依赖于前端路由来完成!
10.1.2 什么是前端路由
Hash地址与组件之间的对应关系

图是参考这篇文章:vue-router 路由超详细教程_vue router-CSDN博客
10.2 Vue Router的安装
10.2.1 本地独立版本方法
可通过地址"https://unpkg.com/vue-router@next"将最新版本的Vue Router库(vue-router.global.js)下载到本地(在页面上右击,在弹出的快捷菜单中选择另存为),编写本书时,最新版本是4.0.13。然后,在界面文件中引入vue-router.global.js库,示例代码如下。
<script src="js/vue-router.global.js"></script>
10.2.2 CDN方法
在界面文件中可通过CDN(Content Delivery Network,内容分发网络)引入最新版本的Vue Router库,示例代码如下。
<script src="https://unpkg.com/vue-router@next"></script>
对于生产环境,建议使用固定版本,以免因版本不同带来兼容性问题,示例代码如下。
<script src="https://unpkg.com/vue-router@4.0.13/dist/vue-router.global.js"></script>
10.2.3 NPM方法
在使用Vue.js构建大型应用时推荐使用NPM安装最新稳定版的Vue Router,因为NPM能很好地和webpack模块打包器配合使用,示例如下。
npm install vue-router@next
10.2.4 命令行工具(Vue CLI)方法
为提高单页面应用程序的开发效率,我们现在开始使用Vue CLI(Vue 脚手架)搭建Vue.js项目。
Vue CLI是一个基于Vue.js进行快速开发的完整系统,提供如下功能。
l 通过@vue/cli实现交互式项目脚手架;
l 通过@vue/cli + @vue/cli-service-global实现零配置原型开发;
l 一个运行时依赖@vue/cli-service,该依赖可升级,基于webpack构建,并带有合理的默认配置;可通过项目的配置文件进行配置;可通过插件进行扩展;
l 一个丰富的官方插件集合,集成了前端生态工具;
l 提供一套创建和管理Vue.js项目的用户界面。
Vue CLI 致力于将Vue.js生态工具基础标准化。确保各种构建工具平稳衔接,让开发者专注在撰写应用上,而不必纠结配置的问题。
1.全局安装Vue CLI
打开cmd命令行窗口,输入命令npm install -g @vue/cli全局安装Vue脚手架,输入命令vue --version查看版本(测试是否安装成功)。如果需要升级全局的 Vue CLI,在cmd命令行窗口运行npm update -g @vue/cli命令即可。
2.打开图形化界面
安装成功后,在命令行窗口,继续输入命令vue ui打开一个浏览器窗口,并以图形化界面引导至项目创建的流程。

3.创建项目--创建项目界面




4.使用VSCode打开项目
使用VSCode打开(File ---> Open Folder,选择项目目录)第3步创建的项目router-demo。打开后,在Terminal终端输入npm run serve命令启动服务。

5.运行项目
在浏览器地址栏中,访问http://localhost:8080/即可运行项目router-demo。通过http://localhost:8080/访问时,打开的页面是public目录下的index.html。index.html是一个普通的html文件,让它与众不同的是"<div id="app"></div>"这句程序,下面有一行注释,构建的文件将会被自动注入,也就是说我们编写的其他的内容都将在这个div中展示。另外,整个项目只有这一个html文件,所以这是一个单页面应用,当我们打开这个应用,表面上可以看到很多页面,实际上它们都在这一个div中显示。
在main.js中,创建了一个Vue对象。该Vue对象的挂载目标是"#app"(与index.html中的id="app"对应);router代表该对象包含Vue Router,并使用项目中定义的路由(在src/router目录下的index.js文件里定义)。
10.3 Vue Router的基本用法
使用Vue Router动态加载不同组件时,需要将组件(Components)映射到路由(Routers),然后告诉Vue Router在哪里显示它们。
vue-router
是 vue.js 官方给出的路由解决方案
。它只能结合 vue 项目进行使用,能够轻松的管理 SPA 项目中组件的切换。
10.3.1 路由安装
① 安装 vue-router 包
bash
npm install vue-router@4

② 创建路由模块(vue3.0)

在 src 源代码目录下,新建 router/index.js 路由模块,并初始化如下的代码:
javascript
import { createRouter, createWebHistory } from 'vue-router'
//导入组件
import SecondView from '../views/SecondView.vue'
import ThirdView from '../views/ThirdView.vue'
//定义路由
const routes = [
{
path: '/first',
name: 'first',
//导入组件
component: () => import('../views/FirstView.vue')
},
{
path: '/second/:uname/:pwd',
name: 'second',
//导入组件
component: SecondView
},
{
path: '/third/:uname/post/:pwd/post/:age',
name: 'third',
//导入组件
component: ThirdView
}
]
//创建路由实例router(管理路由),传入routes配置
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes
})
export default router
③ 导入并挂载路由模块

在 src/main.js 入口文件中,导入并挂载路由模块。示例代码如下:
javascript
import './assets/main.css'
// import Vue from 'vue'
// import App from './App.vue'
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
const app = createApp(App)
app.use(router)
app.mount('#app')
④ 声明路由链接和占位符

在 src/App.vue 组件中,使用 vue-router 提供的 <router-link>
和 <router-view>
声明路由链接和占位符:
javascript
<template>
<nav>
<router-link to="/first?uname=chenheng&pwd=123456">第一个页面</router-link> |
<router-link to="/second/chenheng1/654321">第二个页面</router-link>|
<router-link to="/third/:张三/post/:654321/post/:18">第三个页面</router-link>
</nav>
<!--router-view表示路由出口,将匹配到的组件(相当于链接的页面)渲染在这里。 -->
<router-view/>
</template>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
}
nav {
padding: 30px;
}
nav a {
font-weight: bold;
color: #2c3e50;
}
nav a.router-link-exact-active {
color: #42b983;
}
</style>
10.3.2 跳转与传参
Vue Router有两种跳转,第一种是使用内置的<router-link>组件,默认渲染一个<a>标签,示例代码如下。

<router-link>组件与一般组件一样,to是一个prop,指定跳转的路径。使用<router-link>组件,在HTML5的History模式下将拦截点击,避免浏览器重新加载页面。
Vue Router的第二种跳转方式需要在JavaScript里进行,类似于window.location.href。这种方式需要使用router实例方法push或replace。
javascript
Vue Router的第二种跳转方式需要在JavaScript里进行,类似于window.location.href。这种方式需要使用router实例方法push或replace。
<template>
<div>第一个页面</div>
<button @click="goto">去第二个页面</button>
</template>
<script>
export default {
methods: {
goto () {
// 也可以使用replace方法,与replace属性一样不会向history添加新记录
this.$router.push('/MView2')
}
}
}
路由传参,一般有两种方式:query和params。不管哪种方式都可以是通过修改URL来实现。
(1)query传参
query传递参数的示例代码如下:
<router-link to="/?id=888&pwd=999">
通过$route.query获取路由中的参数,示例代码如下:
<h4>id:{{$route.query.id}}</h4>
<h4>pwd:{{$route.query.pwd}}</h4>

(2)params传参
在路由规则中定义参数,修改路由规则的path属性(动态匹配),示例代码如下。
{
path: '/:id/:pwd',
name: 'MView1',
component: MView1
}
<router-link to="/888/999">
通过$route.params获取路由中的参数,示例代码如下:
<h4>id:{{$route.params.id}}</h4>
<h4>pwd:{{$route.params.pwd}}</h4>



10.3.3 配置路由
路由配置,通常在前端工程项目的src/router/index.js文件中进行。首先,需要在前端工程项目的src/main.js和src/router/index.js文件中,分别导入vue和vue-router模块,并在main.js中执行use方法注册路由。
10.4 Vue Router高级应用
10.4.1 动态路由匹配
如果有多个参数,即多个冒号,则route.params中保存为对象。例如,路由路径path为/user/:uname/:pwd,则对应的访问路径为/user/zhangsan/123456,route.params中的对象为{ uname: 'zhangsan', pwd: '123456'}。另外,也可以使用post进行多个动态参数传递,例如,路由路径path为/user/:uname/post/:pwd/post/:age,则对应的访问路径为/user/:lisi/post/:654321/post/:18,$route.params中的对象为{ uname: 'lisi', pwd: '123456', age: '18'}。
route路由信息对象表示当前激活的路由状态信息,每次成功导航后都将产生一个新的对象。除了route.params外,$route对象还提供其他许多有用的信息
|---------|-----------------|-----------------------------------------------------------------------------------------------------------|
| 序 号 | 属 性 名 称 | 说 明 |
| 1 | $route.path | 对应当前路由的路径,如/third/:张三/post/:654321/post/:18 |
| 2 | $route.params | 一个key:value对象,包含了所有动态参数,如果没有参数,则是一个空对象,如{ "uname": ":张三", "pwd": ":654321", "age": ":18" } |
| 3 | route.query | 一个key:value对象,表示URL查询参数。例如,/first?uname=chenheng\&pwd=123456,则有route.query.uname为chenheng。如果没有查询参数,则是空对象 |
| 4 | $route.hash | 当前路由的哈希值(不带#),如果没有哈希值,则为空字符串 |
| 5 | $route.fullPath | 完成解析后的URL,包含查询参数和哈希的完整路径 |
| 6 | $route.matched | 返回数组,包含当前匹配的路径中包含的所有片段所对应的配置 |
| 7 | $route.name | 当前路径名称 |
| 8 | $route.meta | 路由元信息 |
10.4.2 嵌套路由
嵌套路由,即路由的多层嵌套,也称为子路由。在实际应用中,嵌套路由相当于多级菜单,一级菜单下有二级菜单,二级菜单下有三级菜单,等等。
首先,在根组件App.vue中定义基础路由(相当于一级菜单)导航;其次,定义基础路由对应的组件;最后,完成所有嵌套路由组件的定义,并在router/index.js文件中定义嵌套路由。
如果'vue-cli-service' 不是内部或外部命令,也不是可运行的程序
npm install -g@vue/cli

详细代码:
代码目录

main.js
javascript
//main.js
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
createApp(App).use(router).mount('#app')
router/index.js
javascript
//router/index.js
import { createRouter, createWebHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue'
import ProductView from '../views/ProductView.vue'
const routes = [
{
path: '/',
name: 'home',
component: HomeView
},
{
path: '/about',
name: 'about',
component: () => import('../views/AboutView.vue')
},
{
path: '/product',
name: 'product',
component: ProductView,
children:[//子路由
{
path: '', //空子路由为基础路由的默认显示
component: () => import('../views/AlldevView.vue')
},
{
path: 'alldev', //注意这里没有'/'
component: () => import('../views/AlldevView.vue')
},
{
path: 'JavaEE',
component: () => import('../views/JavaEEView.vue')
},
{
path: 'SpringBoot',
component: () => import('../views/SpringBoot.vue')
}
]
}
]
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes
})
export default router
app.vue
javascript
//app.vue
<template>
<h1>嵌套路由</h1>
<nav>
<router-link to="/">首页</router-link> |
<router-link to="/about">关于我们</router-link> |
<router-link to="/product">产品介绍</router-link>
</nav>
<router-view class="my-view"> </router-view>
</template>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
}
nav {
padding: 30px;
}
nav a {
font-weight: bold;
color: #2c3e50;
}
nav a.router-link-exact-active {
color: #42b983;
}
.my-view {
width: 500px;
margin: 0 auto;
text-indent: 2em;
text-align: left;
padding: 5px 10px;
border: 1px dashed #42b983;
}
</style>
alldevView.vue
javascript
<template>
<div>
<img alt="alldev" src="../images/091883-all.jpg" width="200" height="300">
</div>
</template>
JavaEEView.vue
javascript
<template>
<div>
<img alt="javaee" src="../images/079720-javaee.jpg" width="200" height="300">
</div>
</template>
ProductView.vue
javascript
<template>
<div>
<p>
<!--定义嵌套路由-->
<router-link to="/product/alldev">全栈开发</router-link> |
<router-link to="/product/JavaEE">Java EE整合开发</router-link> |
<router-link to="/product/SpringBoot">Spring Boot开发</router-link>
</p>
<router-view/>
</div>
</template>
<style scoped>
p a {
text-decoration: none;
}
</style>
SpringBoot.vue
javascript
<template>
<div>
<img alt="springboot" src="../images/083960-springboot.jpg" width="200" height="300">
</div>
</template>
10.4.3 编程式导航
除了使用内置的<router-link>组件,渲染一个<a>标签定义导航链接外,还可以通过编程调用路由(router或this.$router)的实例方法实现导航链接。

前一步
javascript
go1(){
this.$router.forward()
},
后退一步
javascript
back1(){
this.$router.back()
},
回首页
javascript
goHome(){
this.$router.push('/') //字符串路由path
},
看产品介绍
javascript
goProduct(){
this.$router.push({ //对象
path: '/product'
})
},
代替关于我们
javascript
repAbout(){
this.$router.replace({
name: 'home', //命名路由
params:{uname:'123', pwd:'abc'} //传参
})
}

|-------------|-------------|-----------------------------------|----------------------------------------------------------------------------|
| 序 号 | 方 法 名 称 | 功 能 说 明 | |
| 1 | push() | 跳转到由参数指定的新路由地址,在历史记录中添加一条新记录 | |
| 2 | replace() | 跳转到由参数指定的新路由地址,替换当前的历史记录 | |
| 3 | go(n) | n为整数,在历史记录中向前或后退n步 | |
| 4 | forward() | 在历史记录中向前一步,相当于this.router.go(1) |  |
| **5** | back() | 在历史记录中后退一步,相当于this.router.go(-1) | |
push()方法和replace()方法的用法相似,唯一不同的是push()方法在历史记录中添加一条新记录,replace()方法不会添加新记录,而是替换当前记录。点击返回,会跳转到上上一个页面。


上面这个小视频的看产品介绍是push模式,看产品介绍1是replace模式,我们点返回的时候,看出来两个返回是不一样的,一个是上一步,一个调回到百度首页。
push()方法和replace()方法的参数可以是字符串、对象、命名路由、带查询参数等多种形式,示例如下。
//字符串路由path
this.$router.push('/')
//对象
this.$router.push({path: '/product'})
//命名路由及params传参,params更像post,是隐性传参
this.$router.push({name: 'home', params:{uname:'123', pwd:'abc'} })
//带查询参数,/product?uname=123&pwd=abc,更像get传参,是显性传参
this.$router.push({path: '/product', query:{uname:'123', pwd:'abc'} })
this.$router.push({name: 'home', state:{uname:'123', pwd:'abc'} })
在home对应的页面可以使用history.state接收数据。
10.4.4 命名路由
{
path: '/',
name: 'home',
component: HomeView
}
<router-link :to="{name: 'home', params: {uname: '123', pwd: 'abc'}}">首页</router-link>
与编程式导航this.$router.push({name: 'home', params:{uname:'123', pwd:'abc'} })功能相同。
10.4.5 重定向
10.4.6 路由组件props传参

10.4.7 HTML5 历史记录模式
| 对比 | hash 模式 | history 模式 |
| url 显示 | url 中带"#" | url 中不带"#" |
| 回车刷新(浏览器刷新按钮) | 页面正常显示 | 后端未配置则页面显示404 |
支持版本 | 支持低版本浏览器和 IE 浏览器 | HTML5 新推出的 API |
---|
代码对比
10.5 路由钩子函数
10.5.1 全局前置钩子函数
在Vue Router中,使用router.beforeEach注册一个全局前置钩子函数(在路由跳转前执行),注册示例代码如下。

router.beforeEach(async (to, from) => {
//在ES7标准中新增了async和await关键字,作为处理异步请求的一种解决方案
if (
// 检查用户是否已登录
!isAuthenticated &&
// 避免无限重定向
to.name !== 'Login'
) {
// 将用户重定向到登录页面
return { name: 'Login' }
}
})
10.5.2 全局解析钩子函数
在Vue Router中,使用router.beforeResolve注册一个全局解析钩子函数。与router.beforeEach 类似,在每次导航时都会触发,但是确保在导航被确认之前,同时在所有组件内钩子函数和异步路由组件被解析之后,解析钩子函数就被正确调用。
router.beforeResolve(async to => {
if (to.meta.requiresCamera) {
try {
await askForCameraPermission()
} catch (error) {
if (error instanceof NotAllowedError) {
// ... 处理错误,然后取消导航
return false
} else {
//意料之外的错误,取消导航并把错误传给全局处理器
throw error
}
}
}
})
10.5.3 全局后置钩子函数
在Vue Router中,也可以使用router.afterEach注册全局后置钩子函数,该钩子函数不接收next参数,也不会改变导航本身,在跳转之后判断。对于分析、更改页面标题、声明页面等辅助功能都很有用。示例代码如下。
router.afterEach((to, from) => {
// ...
})
10.5.4 某个路由的钩子函数

10.5.5 组件内的钩子函数
const UserDetails = {
template: `...`,
beforeRouteEnter(to, from) {
// 在渲染该组件的对应路由被验证前调用,不能获取组件实例 `this` !因为当该钩子函数执行时,组件实例还没被创建!
},
beforeRouteUpdate(to, from) {
// 在当前路由改变,但是该组件被复用时调用。举例来说,对于一个带有动态参数的路径`/users/:id`,在`/users/1`和 `/users/2`之间跳转的时候,由于渲染同样的`UserDetails` 组件,因此组件实例会被复用,此钩子函数在此情况下也被调用。因为在这种情况发生的时候,组件已经挂载好了,该钩子函数可以访问组件实例 `this`
},
beforeRouteLeave(to, from) {
// 在导航离开渲染该组件的对应路由时调用与 `beforeRouteUpdate` 一样,它可以访问组件实例 `this`
},
}
10.6 路由元信息
有时,可能希望将任意信息附加到路由上,如过渡名称、访问路由权限等。这些工作可以通过接收属性对象的meta属性来实现,并且它可以在路由地址和导航守卫(路由钩子函数)中都能被访问到。

下面我们用一个完整的例子把路由守卫说明白,首先了解路由守卫是干什么的,就如下这个例子,我开始点击home的页签,系统提示我没有登录,然后页面切入到登录的页面,这是一个很常见的拦截器的功能,就是判断是否有登录,在vue我们就用路由守卫干这件事,所以路由守卫其实简单理解就是拦截器。这个最重要的就是router.beforeEach,代码是写在main.JS中


