# VueRouter

Router

路由简介

路由与路由器

  • vue-router: vue 的一个插件库,专门用来实现 SPA 应用

1673879228077

单页面

  • 单页 Web 应用(single page web application,SPA)
  • 整个应用只有一个完整的页面。
  • 点击页面中的导航链接不会刷新页面,只会做页面的局部更新
  • 数据需要通过 ajax 请求获取。

1673879263731

路由的工作流程

1673879293336

什么是路由

  • —个路由就是一组映射关系 (key - value)

  • key 为路径, value 可能是 function 或 component

路由的分类

  • 后端路由:
    • 理解: value 是 function,用于处理客户端提交的请求。
    • 工作过程: 服务器接收到一个请求时,根据请求路径找到匹配的函数来处理请求,返回响应数据。
  • 前端路由:
    • 理解: value 是 component,用于展示页面内容。
    • 工作过程: 当浏览器的路径改变时,对应的组件就会显示。

路由的基本使用

  • 01 安装路由:
1
2
3
npm i vue-router@3
# vue-router 3.x 版本只能在 vue2 中使用,若不指定版本安装,会安装最新版 vue-router 4.X,在vue 2.x 中使用会报错
# vue-router 4.x 版本是能在 vue3 中使用
  • 02 导入并应用路由
  • 04 导入路由配置信息
  • 05 注册路由

main.js:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import Vue from 'vue'
import App from './App.vue'

//01. 引入vue-router
import VueRouter from "vue-router"

//04. 导入路由配置信息
import router from "./router/index.js"

Vue.config.productionTip = false

//02. 应用vue-router插件
Vue.use(VueRouter)

new Vue({
render: h => h(App),
//05. 注册路由
router,
}).$mount('#app')
  • 03 配置路由信息

./src/router/index.js:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 03. 配置路由

// 该文件专门用于创建整个应用的路由器
import VueRouter from "vue-router";

//引入组件
//路由组件一般放置在 ./src/pages 文件夹中
//非路由组件一般放置在 ./src/components 文件夹中
import About from "../pages/About.vue";
import Home from "../pages/Home.vue";

//创建并暴露一个路由器
export default new VueRouter({
routes: [
{
path: "/about",
component: About,
},
{
path: "/home",
component: Home,
},
],
});
  • 06 在App.vue 中:
1
2
3
4
5
6
7
8
9
<div class="list-group">
<!-- 原始html中我们使用a标签实现页面的跳转 -->
<!-- <a class="list-group-item active" href="./about.html">About</a> -->
<!-- <a class="list-group-item" href="./home.html">Home</a> -->

<!-- Vue中借助router-link标签实现路由的切换 -->
<router-link class="list-group-item" active-class="active" to="/about">About</router-link>
<router-link class="list-group-item" active-class="active" to="/home">Home</router-link>
</div>
1
2
3
4
<div class="panel-body">
<!-- 路由出口: -->
<router-view></router-view>
</div>
  • 07 Home.vue:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<template>
<div>
<h1>我是About的内容</h1>
</div>
</template>

<script>
export default {
name:"About",
//组件进行切换时,会进行销毁和创建
beforeDestroy() {
console.log("About组件即将被销毁~~~");
},
mounted() {
console.log("About组件挂载完毕~~");
},
}
</script>

<style scoped lang = "less">

</style>
  • 08 几个注意点
    • 1.路由组件通常存放在 pages 文件夹,一般组件通常存放在 components 文件夹。
    • 2.通过切换,“隐藏””了的路由组件,默认是被销毁掉的,需要的时候再去挂载。
    • 3.每个组件都有自己的 $route 属性,里面存储着自己的路由信息。
    • 4.整个应用只有一个 router,可以通过组件的 $router 属性获取到。

多级路由的嵌套

  • 01 在 Home.vue 组件下嵌套两个路由组件:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<template>
<div>
<div>
<h2>Home组件内容</h2>
<div>
<ul class="nav nav-tabs">
<li>
<router-link
class="list-group-item"
active-class="active"
to="/home/news"
>News</router-link
>
</li>
<li>
<router-link
class="list-group-item"
active-class="active"
to="/home/message"
>Message</router-link
>
</li>
</ul>
<router-view></router-view>
</div>
</div>
</div>
</template>

<script>
export default {
name: "Home",
};
</script>

<style scoped lang="less"></style>
  • 02 配置多级嵌套路由信息
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
//配置路由

// 该文件专门用于创建整个应用的路由器
import VueRouter from "vue-router";

//引入组件
//路由组件一般放置在 ./src/pages 文件夹中
//非路由组件一般放置在 ./src/components 文件夹中
import About from "../pages/About.vue";
import Home from "../pages/Home.vue";
import News from "../pages/News.vue"
import Message from "../pages/Message.vue"

//创建并暴露一个路由器
export default new VueRouter({
routes: [
{
path: "/about",
component: About,
},
{
path: "/home",
component: Home,
children: [
{
path: "news",
component:News
},
{
path: "message",
component:Message
},
]
},
],
});
  • 03 两个子路由文件 News.vueMessage.vue :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<template>
<div>
<ul>
<li>news001</li>
<li>news002</li>
<li>news003</li>
</ul>
</div>
</template>

<script>
export default {
name: "News",
components: {},
};
</script>

<style scoped lang="less"></style>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<template>
<div>
<ul>
<li><a href="/message1">message001</a>&nbsp;&nbsp;</li>
<li><a href="/message2">message002</a>&nbsp;&nbsp;</li>
<li><a href="/message/3">message003</a>&nbsp;&nbsp;</li>
</ul>
</div>
</template>

<script>
export default {
name:"Message",
components: {},
};
</script>

<style scoped lang="less"></style>

路由的query参数

  • 01 新建 Detail.vue 路由组件:
  • 04 在组件中获取 query 参数: [this.]$route.query.xxx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<template>
<div>
<ul>
<li>消息编号:{{$route.query.id}}</li>
<li>消息标题:{{$route.query.title}}</li>
</ul>
</div>
</template>

<script>
export default {
name:"Detail",
}
</script>
  • 02 在 ./src/router/index.js 中配置路由信息,将 Detail.vue 组件配置成 Message.vue 的子路由
1
import Detail from "../pages/Detail.vue";
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
{
path: "/home",
component: Home,
children: [
{
path: "news",
component: News,
},
{
path: "message",
component: Message,
children: [
{
path: "detail",
component: Detail,
},
],
},
],
},
  • 03 路由跳转与传递 query 参数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<template>
<div>
<ul>
<!-- 跳转路由并携带query参数,to的字符串写法 -->
<!-- <li v-for="m in messageList" :key="m.id">
<router-link :to="`/home/message/detail?id=${m.id}&title=${m.title}`">{{
m.title
}}</router-link
>
</li> -->

<!-- 跳转路由并携带query参数,to的对象写法 -->
<li v-for="m in messageList" :key="m.id">
<router-link
:to="{
path: '/home/message/detail',
query: {
id: m.id,
title: m.title,
},
}"
>{{ m.title }}</router-link
>
</li>
</ul>
<hr />
<router-view></router-view>
</div>
</template>

命名路由

  • 01 在 ./src/router/index.js 中配置路由信息中添加 name 属性:
1
2
3
4
5
6
7
8
9
10
11
{
path: "message",
component: Message,
children: [
{
name:"xiangqing",
path: "detail",
component: Detail,
},
],
},
  • 02 在 Message.vue 组件中进行路由跳转时,使用 name 命名路由
1
2
3
4
5
6
7
8
9
10
11
12
13
<!-- 跳转路由并携带query参数,to的对象写法 -->
<li v-for="m in messageList" :key="m.id">
<router-link
:to="{
name: 'xiangqing',
query: {
id: m.id,
title: m.title,
},
}"
>{{ m.title }}</router-link
>
</li>
  • 03 同理在 路由配置信息中配置了 name 属性,在 router-link 标签中也可以使用命名路由进行路由跳转
1
2
3
<!-- Vue中借助router-link标签实现路由的切换 -->
<router-link class="list-group-item" active-class="active" :to="{name:'guanyu'}">About</router-link>
<router-link class="list-group-item" active-class="active" to="/home">Home</router-link>

params参数

  • 01 在 ./src/router/index.js 中配置路由信息的 path 中进行参数占位
1
2
3
4
5
6
7
8
9
10
11
{
path: "message",
component: Message,
children: [
{
name:"xiangqing",
path: "detail/:id/:title",
component: Detail,
},
],
},
  • 02 在 Message.vue 路由组件中进行路由传参跳转
    • 特别注意:路由携带 params 参数时,若使用 to 的对象写法,则不能使用 path 配置项,必须使用 name 配置!
1
2
3
4
5
6
7
 <!-- 跳转路由并携带params参数,to的字符串写法 -->
<li v-for="m in messageList" :key="m.id">
<router-link :to="`/home/message/detail/${m.id}/${m.title}`">{{
m.title
}}</router-link
>
</li>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<template>
<div>
<ul>
<!-- 跳转路由并携带query参数,to的对象写法 -->
<li v-for="m in messageList" :key="m.id">
<router-link
:to="{
name: 'xiangqing',
//若使用to的对象写法,用params进行路由传参,必须使用name命名路由进行跳转
params: {
id: m.id,
title: m.title,
},
}"
>{{ m.title }}</router-link
>
</li>
</ul>
<hr />
<router-view></router-view>
</div>
</template>

<script>
export default {
name: "Message",
components: {},
data() {
return {
messageList: [
{ id: "001", title: "消息001" },
{ id: "002", title: "消息002" },
{ id: "003", title: "消息003" },
],
};
},
};
</script>
  • 03 在 Detail.vue 组件中获取 params 参数: [this.]$route.params.xxx
1
2
3
4
5
6
7
8
<template>
<div>
<ul>
<li>消息编号:{{$route.params.id}}</li>
<li>消息标题:{{$route.params.title}}</li>
</ul>
</div>
</template>

路由的props配置01

01 在 ./src/router/index.js 中配置路由信息中配置 props

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
{
path: "/home",
component: Home,
children: [
{
path: "news",
component: News,
},
{
path: "message",
component: Message,
children: [
{
name:"xiangqing",
path: "detail/:id/:title",
component: Detail,
//props 的第一种写法,值为对象,该对象中的所有key-value都会以props的形式传给Detail组件。
props: {id:1,title:"hello"}
},
],
},
],
},

02 在 Detail.vue 组件中获取 props 参数,并使用

1
props:["id","title"],
1
2
3
4
5
6
7
8
<template>
<div>
<ul>
<h1>{{id}}</h1>
<h1>{{title}}</h1>
</ul>
</div>
</template>

路由的props配置02

01 在 ./src/router/index.js 中配置路由信息中配置props

1
2
3
4
5
6
7
8
9
10
11
12
13
{
path: "message",
component: Message,
children: [
{
name:"xiangqing",
path: "detail/:id/:title",
component: Detail,
//props的第二种写法:值为布尔值,若布尔值为真,就会把该路由组件收到的所有params参数,以props的形式传给Detail组件
props:true
},
],
},

02 在 Detail.vue 组件中获取 props 参数,并使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<template>
<div>
<ul>
<h1>{{id}}</h1>
<h1>{{title}}</h1>
</ul>
</div>
</template>

<script>
export default {
props:["id","title"],
name:"Detail",
}
</script>

路由的props配置03

01 在 Message.vue 组件中,跳转路由并携带 query 参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<template>
<div>
<ul>
<!-- 跳转路由并携带query参数,to的对象写法 -->
<li v-for="m in messageList" :key="m.id">
<router-link
:to="{
name: 'xiangqing',
//若使用to的对象写法,用params进行路由传参,必须使用name命名路由进行跳转
query: {
id: m.id,
title: m.title,
},
}"
>{{ m.title }}</router-link
>
</li>
</ul>
<hr />
<router-view></router-view>
</div>
</template>

<script>
export default {
name: "Message",
components: {},
data() {
return {
messageList: [
{ id: "001", title: "消息001" },
{ id: "002", title: "消息002" },
{ id: "003", title: "消息003" },
],
};
},
};
</script>

02 在 ./src/router/index.js 中配置路由信息中配置 props

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
{
path: "message",
component: Message,
children: [
{
name: "xiangqing",
path: "detail/:id/:title",
component: Detail,

//props的第三种写法: 值为函数
props($route) {
return { id: $route.query.id, title: $route.query.title };
},
//第三种写法的连续解构赋值
//props({ query:{ id,title}}) {
// return { id: id, title: title };
//},
},
],
},

03 在 Detail.vue 组件中获取 props 参数,并使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<template>
<div>
<ul>
<h1>{{id}}</h1>
<h1>{{title}}</h1>
</ul>
</div>
</template>

<script>
export default {
props:["id","title"],
name:"Detail",
}
</script>

routerLink的replace

  • 1.作用: 控制路由跳转时操作浏览器历史记录的模式

  • ⒉浏览器的历史记录有两种写入方式:分别为 push 和 replace, push 是追加历史记录,replace 是替换当前记录。路由跳转时候默认为 push

  • 3.如何开启 replace 模式: <router-link replace .... . .. >News</router-link>

1
2
3
4
5
<div class="list-group">
<!-- Vue中借助router-link标签实现路由的切换 -->
<router-link replace class="list-group-item" active-class="active" :to="{name:'guanyu'}">About</router-link>
<router-link replace class="list-group-item" active-class="active" to="/home">Home</router-link>
</div>

编程式路由导航

  • 作用:不借助<router-link>实现路由跳转,让路由跳转更加灵活

Message.vue:

1
2
<button @click="pushShow(m)">push查看</button>
<button @click="replaceShow(m)">replace查看</button>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
methods: {
pushShow(m) {
this.$router.push({
name:"xiangqing",
params: {
id: m.id,
title: m.title,
},
});
},
replaceShow(m){
this.$router.replace({
name:"xiangqing",
params: {
id: m.id,
title: m.title,
},
});
}
},

banner.vue:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<template>
<div class="col-xs-offset-2 col-xs-8">
<div class="page-header">
<h2>Vue Router Demo</h2>
<button @click="back">后退</button>
<button @click="forward">前进</button>
<button @click="test">测试一下go</button>
</div>
</div>
</template>

<script>
export default {
name:'Banner',
methods: {
back(){
this.$router.back()//后退一
},
forward(){
this.$router.forward()//前进一
},
test(){
this.$router.go(3)//可以前进可以后退
}
},
}
</script>

缓存路由组件

  • 01 添加 keep-live 组件包裹 router-view 组件即可,被缓存的组件不会被销毁(不会调用 beforeDestroy(){} 方法)

  • 02 不添加 include 属性,则会缓存所有在这里显示的路由组件

  • 03 添加 include 属性,则被指定的路由组件才会被缓存,属性值为组件的 name 属性值

1
2
3
<keep-alive include="News">
<router-view></router-view>
</keep-alive>
  • 04 缓存多个组件
1
2
3
<keep-alive :include="['News','Message']">
<router-view></router-view>
</keep-alive>

组件特有的两个生命周期

1
2
3
4
5
6
7
8
activated() {
//当显示该组件时调用
console.log("News被激活了");
},
deactivated() {
//当从该组件切走时调用
console.log("News失活了");
}

前置和后置路由守卫

  • 作用:对路由进行权限控制

  • 分类:全局守卫、独享守卫、组件内守卫

./src/router/index.js 中配置路由信息中配置:

1
2
3
4
5
6
7
{
name:'guanyu',
path:'/about',
component:About,
//路由元信息
meta:{isAuth:true,title:'关于'}
},
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//全局前置路由守卫————初始化的时候被调用、每次路由切换之前被调用
router.beforeEach((to,from,next)=>{
console.log('前置路由守卫',to,from)
if(to.meta.isAuth){ //判断是否需要鉴权
if(localStorage.getItem('school')==='atguigu'){
next()//要调用next(),才能放行
}else{
alert('学校名不对,无权限查看!')
}
}else{
next()
}
})

//全局后置路由守卫————初始化的时候被调用、每次路由切换之后被调用
router.afterEach((to,from)=>{
console.log('后置路由守卫',to,from)
document.title = to.meta.title || '硅谷系统'
})

独享路由守卫

  • beforeEnter:() => {}
  • 写在路由配置信息中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
name:'xinwen',
path:'news',
component:News,
meta:{isAuth:true,title:'新闻'},
beforeEnter: (to, from, next) => {
console.log('独享路由守卫',to,from)
if(to.meta.isAuth){ //判断是否需要鉴权
if(localStorage.getItem('school')==='atguigu'){
next()
}else{
alert('学校名不对,无权限查看!')
}
}else{
next()
}
}
},

组件内守卫

  • 写在组件中:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//通过路由规则,进入该组件时被调用
beforeRouteEnter (to, from, next) {
console.log('About--beforeRouteEnter',to,from)
if(to.meta.isAuth){ //判断是否需要鉴权
if(localStorage.getItem('school')==='atguigu'){
next()
}else{
alert('学校名不对,无权限查看!')
}
}else{
next()
}
},

//通过路由规则,离开该组件时被调用
beforeRouteLeave (to, from, next) {
console.log('About--beforeRouteLeave',to,from)
next()
}

history模式和hash模式

  • 对于一个 url 来说,什么是 hash 值?
    • # 及其后面的内容就是 hash 值。
  • hash 值不会包含在 HTTP 请求中,即: hash 值不会带给服务器。
  • hash 模式:
    • 地址中永远带着 # 号,不美观。
    • 若以后将地址通过第三方手机 app 分享,若 app 校验严格,则地址会被标记为不合法。
    • 兼容性较好。
  • history 模式:
    • 地址干净,美观。
    • 兼容性和 hash 模式相比略差。
    • 应用部署上线时需要后端人员支持,解决刷新页面服务端 404 的问题。
  • 在路由配置信息中的 mode 属性决定,可选值: history | hash, 默认是 hash 模式
  • 用 hash 模式,node 服务器部署静态资源时,刷新页面不会 404
  • 用 history 模式,node 服务器部署静态资源时,刷新页面会 404
    • 可以使用 node 中间件 connect-history-api-fallback 解决 history 模式刷新 404 的问题
1
2
3
4
5
6
7
8
9
10
11
12
13
14
//创建并暴露一个路由器
export default new VueRouter({
mode:"hash",
routes: [
{
path: "/about",
component: About,
},
{
path: "/home",
component: Home,
},
],
});
  • connect-history-api-fallback 中间件的使用:
1
npm i connect-history-api-fallback
1
2
3
const history = require( ' connect-history-api-fallback');

app.use(history()) //必须在托管静态资源之前使用