【Vue】系列四 - 路由


一、路由

  • 路由器(router):多个路由需要经过路由器的管理。

  • 路由(route):一组映射关系(key-value),key为路径,value可能是function(针对后端)或component(针对前端)。

    • 后端路由:valuefunction,用于处理客户端提交的请求。
      • 工作过程:服务器接收到一个请求时,会根据请求路径找到匹配的函数来处理请求,然后返回响应数据
    • 前端路由:valuecomponent,用于展示页面内容。
      • 工作过程:当浏览器的路径改变时,对应的组件就会显示
  • SPA(Single Page Application):单页面应用程序,整个应用只有一个完整的页面。Vue和React都是单页面应用,因为只有一个html文件,页面跳转就是依赖路由控制。

二、vue-router

vue-router是Vue的一个插件库,专门用来实现SPA应用。

2.1. 基本使用

  • 安装:npm install vue-router@3 (vue-router@4对应Vue3,vue-router@3对应Vue2)

  • 创建路由器

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    // src/router/index.js 该文件专门用于创建整个应用的路由器
    import VueRouter from 'vue-router'
    import About from '../components/About'
    import Home from '../components/Home'

    // 创建一个路由器
    export default new VueRouter({
    routes: [
    {
    path: '/about',
    component: About
    },
    {
    path: '/home',
    component: Home
    }
    ]
    })
  • 绑定

    1
    2
    3
    4
    5
    6
    7
    import router from './router'

    new Vue({
    el: '#app',
    render: h => h(App),
    router: router
    })
  • 使用

    普通的a标签跳转页面肯定是不行的,只能通过vue-router提供的router-link替换a标签。

    router-link:实现页面的切换,属性active-class可以配置高亮样式。

    router-view:指定组件的显示位置。

    1
    2
    3
    4
    <!-- <a class="active" href="./about.html">About</a> -->
    <router-link active-class="active" to='/about'>About</router-link>

    <router-view></router-view>

路由组件被路由切换时,在需要的时候再去挂载,隐藏的时候会被销毁。

路由组件被挂载后,组件实例会新增两个属性:$route$router

$route:路由组件的配置信息,每个路由组件都有该属性,且配置都不一样。

$router:路由管理,整个应用只有一个,每个路由组件都有该属性,配置都一样。

2.2. 嵌套路由

嵌套路由使用children配置项:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
export default new VueRouter({
routes: [
{
path: '/about',
component: About
},
{
path: '/home',
component: Home,
children: [
{
path: 'news', // 此处不能写/news
component: News
},
{
path: 'message',
component: Message
}
]
}
]
})

注意:children中的配置项path前面不能加/

使用嵌套路由跳转:

1
<router-link to="/home/news">News</router-link>

2.3. 命名路由

作用:使用路由的name配置项可以简化路由的跳转。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
export default new VueRouter({
routes: [
{
path: '/about',
component: About
},
{
path: '/home',
component: Home,
children: [
{
name: 'news', // 路由命名
component: News
},
{
path: 'message',
component: Message
}
]
}
]
})

使用name跳转:

1
2
3
4
5
6
7
8
9
10
11
<!-- 简化前 -->
<router-link to="/home/news">跳转</router-link>

<!-- 简化后 -->
<router-link :to="{
name: 'news',
query: {
id: 123,
title: '你好'
}
}">跳转</router-link>

2.4. 路由参数

传参有两种方式:query和params。

2.4.1. query

使用query传参,会把参数暴露在页面路径上,如:http://localhost:8080/home/news/123/你好

传递参数:

1
2
3
4
5
6
7
8
9
10
11
<!-- to:字符串写法 -->
<router-link to="/home/news?id=123&title=你好">跳转</router-link>

<!-- to:对象写法 -->
<router-link :to="{
path: '/home/news',
query: {
id: 123,
title: '你好'
}
}">跳转</router-link>

接收参数:

1
2
<h2>{{$route.query.id}}</h2>
<h2>{{$route.query.title}}</h2>

2.3.2. params

使用params传参,不会暴露传递参数。

配置路由,声明接收params参数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
export default new VueRouter({
routes: [
{
path: '/about',
component: About
},
{
path: '/home',
component: Home,
children: [
{
name: 'news',
path: 'news/:id/:title', // 使用占位符声明接收params参数
component: News,
},
{
path: 'message',
component: Message
}
]
}
]
})

传递参数:

1
2
3
4
5
6
7
8
9
10
11
<!-- to:字符串写法 -->
<router-link to="/home/news/123/你好">跳转</router-link>

<!-- to:对象写法,必须使用name -->
<router-link :to="{
name: 'news',
params: {
id: 123,
title: '你好'
}
}">跳转</router-link>

接收参数:

1
2
<h2>{{$route.params.id}}</h2>
<h2>{{$route.params.title}}</h2>

注意:路由携带params参数时,若使用to的对象写法,则不能使用path配置项,必须使用name配置。

2.5. 路由props

作用:让路由组件更方便的接收参数。

场景:如果使用路由跳转到一个组件需要传递几十个参数,就需要在组件内写很多重复代码,而使用props就可以让组件直接使用路由传递过来的参数。

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
39
export default new VueRouter({
routes: [
{
path: '/about',
component: About
},
{
path: '/home',
component: Home,
children: [
{
name: 'news',
path: 'news/:id/:title', // 使用占位符声明接收params参数
component: News,
// 第一种写法:props值是对象。该对象中所有的key-value组合最终都会通过props传递给News组件。
// 特点:固定传值
// props: {test: 123}

// 第二种写法:props值是布尔。如果为true,把路由收到的所有params参数通过props传给News组件
// props: true

// 第三种写法:props值是函数。该函数返回的对象中每一组key-value都会通过props传给Detail组件
// 函数接收一个路由对象:代表的是该组件的路由
// 函数返回值:对象
props(route) {
return {
id: route.query.id,
title: route.query.title
}
}
},
{
path: 'message',
component: Message
}
]
}
]
})

2.6. replace

<router-link>有一个replace属性(true/false)。作用是控制路由跳转时操作浏览器历史记录的模式。

浏览器的历史记录有两种写入方式:pushreplace

push:添加历史记录。路由跳转时默认为push

replace:替换当前记录。

1
2
3
4
5
<!-- 完整写法 -->
<router-link :replace="true">News</router-link>

<!-- 简写 -->
<router-link replace>News</router-link>

2.7. 自由跳转

编程式路由导航,作用是不借助<router-link>实现路由跳转,使用自由编码方式让路由跳转更加灵活。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
methods() {
toNews() {
this.$router.push({
name: 'news',
params: {
id: 123,
title: '你好'
}
})
}
}

// 前进
this.$router.forward()
// 后退
this.$router.back()
// 参数:负数代表back;正数代表forward;无参或0代表reload
this.router.go()

2.8. 缓存路由组件

<keep-alive>为了让组件一直保持挂载状态,不被销毁。

1
2
3
4
5
6
7
<keep-alive include="News">
<router-view></router-view>
</keep-alive>

<keep-alive :include="['News', 'Message']">
<router-view></router-view>
</keep-alive>

keep-alive不仅还有include,还有exclude,并且都支持绑定/正则/数组。

使用<keep-alive>会有两个Vue生命周期被用到:

  • activated:路由组件被激活时触发。
  • deactivated:路由组件失活时触发。

三、路由守卫

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

3.1. 全局守卫

针对每一个路由跳转前后进行拦截。

两个关键函数:beforeEachafterEach

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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
// src/router/index.js
export default new VueRouter({
routes: [
{
path: '/about',
component: About,
meta: { // 自定义参数
isAuth: true,
title: '关于'
}
},
{
path: '/home',
component: Home
}
]
})

// src/App.vue
import router from './router'

// 全局前置守卫:初始化时执行、每次路由切换前执行
// to: 到达的路由
// from: 从哪跳转的路由
// next: 函数,控制是否进行路由跳转
router.beforeEach((to, from, next) => {
if (to.meta.isAuth) { // 判断当前路由是否需要进行权限控制
if (localStorage.getItem('token') === '服务器token') {
next() // 放行
} else {
alert('请登录/暂无权限查看')
}
} else {
next() // 放行
}
})

// 全局后置守卫:初始化时执行、每次路由切换前执行
// to: 到达的路由
// from: 从哪跳转的路由
router.afterEach((to, from) => {
if (to.meta.title) {
document.title = to.meta.title // 修改网页的title
} else {
document.title = 'Vue测试' // 注意:需要同步修改package.json的name 或 index.html中的title
}
})

new Vue({
el: '#app',
render: h => h(App),
router: router
})

3.2. 独享守卫

针对路由配置项进行单独配置。用法和全局守卫基本一致。

关键函数:beforeEnter

注意:独享守卫只有前置,没有后置。如果需要后置守卫需要使用全局守卫。

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
export default new VueRouter({
routes: [
{
path: '/about',
component: About,
meta: { // 自定义参数
isAuth: true,
title: '关于'
},
beforeEnter: (to, from, next) => {
if (to.meta.isAuth) { // 判断当前路由是否需要进行权限控制
if (localStorage.getItem('token') === '服务器token') {
next() // 放行
} else {
alert('请登录/暂无权限查看')
}
} else {
next() // 放行
}
}
},
{
path: '/home',
component: Home
}
]
})

3.3. 组件内守卫

针对组件内进行配置的守卫。通过路由规则切换路由时可进行监听配置。

关键函数:beforeRouteEnterbeforeRouteLeave

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<script>
export default {
name: 'About',
// 进入守卫:通过路由规则【进入】该组件时被调用
beforeRouteEnter(to, from, next) {
if (to.meta.isAuth) { // 判断当前路由是否需要进行权限控制
if (localStorage.getItem('token') === '服务器token') {
next() // 放行
} else {
alert('请登录/暂无权限查看')
}
} else {
next() // 放行
}
},
// 离开守卫:通过路由规则【离开】该组件时被调用
beforeRouteLeave(to, from, next) {
next()
}
}
</script>

注意:组件内守卫没有前置后置这一说法,只有进入和离开的概念,一定要注意这两者的区别。

四、路由器的工作模式

路由器由两种工作模式:hashhistory

如:http://localhost:8080/#/home/message/detail

对于一个URL来说,什么是hash值?#及后面的内容就是hash值(和数据结构中的hash概念不一样,但有一点是一样的:唯一性)。

hash值不会包含在HTTP请求中,即hash值不会带给服务器。

hashhistory的区别:

  • hash
    • 地址中永远带着#,不美观
    • 如果以后把地址通过第三方手机APP打开,如果APP校验严格,该地址可能会无法正常加载
    • 兼容较好
  • history
    • 地址干净美观
    • 兼容性和hash模式相比略差
    • 应用部署上线时需要后端人员的支持,解决刷新页面服务端404问题

修改路由模式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// src/router/index.js
export default new VueRouter({
mode: 'hash'// hash或history,默认是history
routes: [
{
path: '/about',
component: About
},
{
path: '/home',
component: Home
}
]
})

五、UI组件库

移动端常用组件库:

PC端常用UI组件库: