1.环境安装

1
2
3
"@vue/cli": "4.5.9"
"vue": "^2.6.11",
"vue-router": "^3.4.9"
1
2
3
npm i @vue/cli
# 安装完脚手架后可以根据cli进行项目创建,并在创建项目过程中选择vue和vue-router的安装
npm i vue-router --save # 也可手动安装vue-router

2.基础使用

2.1安装VueRouter

1.npm install vue-router –save
2.新建src/router/index.js
3.在index.js中为Vue安装路由功能,并导出需要的VueRouter
4.在main.js中引入VueRouter实例
5.将VueRouter实例对象挂载到Vue实例中

index.js

1
2
3
4
5
6
7
8
import Vue from 'vue'
import VueRouter from 'vue-router' //导入模块
Vue.use(VueRouter) //在Vue中安装路由

//导出路由,方便Vue实例进行挂载
export default new VueRouter({
routes: [] //在此处配置路由信息
})

main.js

1
2
3
4
5
6
7
8
9
10
11
12
13
import Vue from 'vue'
import App from './App.vue'
import router from './router' //引入导出的VueRouter,默认是根据文件夹下的index.js

Vue.config.productionTip = false

// 创建和挂载根实例。
// 记得要通过 router 配置参数注入路由,
// 从而让整个应用都有路由功能
new Vue({
render: h => h(App),
router, //挂载到Vue实例中
}).$mount('#app')

2.2配置VueRouter

新建两个组件Home.vue,About.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
<!-- Home.vue -->
<template>
<div>
<h1>首页</h1>
<div>这是首页内容</div>
</div>
</template>

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

<style scoped>

</style>
-----------------------------------------------
<!-- About.vue -->
<template>
<div>
<h1>关于</h1>
<div>这是关于我的相关信息</div>
</div>
</template>

<script>
export default {
name: "About"
}
</script>

<style scoped>

</style>

配置路由映射: 组件和路径映射关系

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
import Vue from 'vue'
import VueRouter from 'vue-router'
//引入所创建的组件
import Home from "@/components/Home";
import About from "@/components/About";

Vue.use(VueRouter)

export default new VueRouter({
//配置路由映射关系
routes: [
{
path: '/home',
name: 'home',
component: Home
},
{
path: '/about',
name: 'about',
component: About
}
]
})

2.3.使用VueRouter

在Vue实例组件中的template使用

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
<template>
<div id="app">
<h1>Jelly's Website</h1>
<!-- 使用 router-link 组件来导航. -->
<!-- 通过传入 `to` 属性指定链接. -->
<!-- <router-link> 默认会被渲染成一个 `<a>` 标签 -->
<!-- tag: tag可以指定<router-link>之后渲染成什么组件,如 tag="button" -->
<!-- 当router-link标签被点击激活后,该标签会自动添加上一个两个class -->
<!-- 其中router-link-active可用于为激活的标签设定指定样式 -->
<router-link to="/home">首页</router-link>
<router-link to="/about">关于</router-link>

<!-- 路由出口 -->
<!-- 路由匹配到的组件将动态渲染在这里 -->
<router-view></router-view>

<div>@2020 by Jelly</div>
</div>
</template>

<script>


export default {
name: 'App',
components: {

}
}
</script>

<style>

</style>

2.4渲染结果

image-20201124121733036image-20201124121749958

需要注意的是,当访问localhost:8080/ 根路径时,是没有视图渲染在router-view中的,可以手动为根路径配置路由重定向到所渲染的视图。

如下:

1
2
3
const routes = [
{path: '/', redirect: '/home'} //path亦可为 path: ''
]

2.5使用javascript方式跳转

有时候, 页面的跳转可能需要执行对应的JavaScript代码, 这个时候不用<router-link>这种方式, 可以使用javascript跳转方式。

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
<template>
<div id="app">
<h1>Jelly's Website</h1>
<!-- 使用 router-link 组件来导航. -->
<!-- 通过传入 `to` 属性指定链接. -->
<!-- <router-link> 默认会被渲染成一个 `<a>` 标签 -->
<router-link to="/home">首页</router-link>
<router-link to="/about">关于</router-link>
<br/>

<!-- 路由出口 -->
<!-- 路由匹配到的组件将动态渲染在这里 -->
<router-view></router-view>

<!-- javascript方式的路由跳转 -->
<button @click="linkToHome">Home</button>
<button @click="linkToAbout">About</button>

<div>@2020 by Jelly</div>
</div>
</template>

<script>


export default {
name: 'App',
methods: {
linkToHome(){
this.$router.push('/home') //栈结构,可以通过back()跳转返回
},
linkToAbout(){
this.$router.replace('/about') //替代,无法跳转返回
},
//es6的语法格式,此处无法使用,this中无router
//linkToAbout: ()=>this.$router.replace('/about') //替代,无法跳转返回
}
}
</script>

<style>

</style>

运行结果:

image-20201124123854699

2.6路径url改变的两种方式

  • 通过 location.hash = 'newURL' 来改变当前页面的url但不发生跳转
  • 通过 history.pushState({},'','/newURL)方式改变,更多使用方式不在次列举

image-20201124124321493

分别执行上图两条语句后,页面url相应变成 http://localhost:8080/about#/homehttp://localhost:8080/about 但是不发生跳转,前者会拼接上一个锚点#,后者是Html5的history模式

默认vue-router使用的是hash锚点的方式,可以在配置文件中更改为history的方式。如下

1
2
3
4
5
6
7
8
9
10
11
12
//路由实例index.js中
import Vue from 'vue'
import VueRouter from 'vue-router'
//引入所创建的组件
Vue.use(VueRouter)

export default new VueRouter({
routes: [

],
mode: 'history' //添加这条语句指定使用history
})

2.7动态路由匹配

我们经常需要把某种模式匹配到的所有路由,全都映射到同个组件。例如,我们有一个 User 组件,对于所有 ID 各不相同的用户,都要使用这个组件来渲染。那么,我们可以在 vue-router 的路由路径中使用“动态路径参数”(dynamic segment) 来达到这个效果:

1
2
3
4
5
6
7
8
9
10
const User = {
template: '<div>User</div>'
}

const router = new VueRouter({
routes: [
// 动态路径参数 以冒号开头
{ path: '/user/:id', component: User }
]
})

现在呢,像 /user/foo/user/bar 都将映射到相同的路由。

一个“路径参数”使用冒号 : 标记。当匹配到一个路由时,参数值会被设置到 this.$route.params,可以在每个组件内使用。于是,我们可以更新 User 的模板,输出当前用户的 ID:

1
2
3
4
const User = {
template: '<div>User {{ $route.params.id }}</div>'
//这个$route 是当前哪个路由处于活跃状态,就是指的哪个路由,并且不需要加上this.
}

更详细的内容见官网

3.进阶使用

3.1懒加载

当打包构建应用时,JavaScript 包会变得非常大,影响页面加载。如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就更加高效了。路由懒加载的主要作用就是将路由对应的组件打包成一个个的js代码块。只有在这个路由被访问到的时候, 才加载对应的组件

结合 Vue 的异步组件和 Webpack 的代码分割功能,轻松实现路由组件的懒加载。

结合二者后的使用如下:

1
2
3
4
5
6
7
8
const Home = () => import('../components/Home')
//路由配置中什么都不需要改变,只需要像往常一样使用
const router = new VueRouter({
routes: [
//懒加载的方式,不同组件会被打包到不同的js文件中,只有使用的时候才会向服务器请求并加载
{path: '/home',name: 'home',component: Home},
]
})

3.2嵌套路由

为/home 嵌套如下两个route,/home/dashboard以及/home/log

新建两个组件DashBoard.vue、Log.vue以及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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
<!-- DashBoard.vue -->
<template>
<div>
这是控制面板
</div>
</template>

<script>
export default {
name: "DashBoard"
}
</script>

<style scoped>

</style>

<!-- Log.vue -->
<template>
<div>
这是日志记录
</div>
</template>

<script>
export default {
name: "Log"
}
</script>

<style scoped>

</style>

<!-- 并更改Home.vue为 -->
<template>
<div>
<h1>首页</h1>
<div>这是首页内容</div>
<!-- 首页内容嵌套路由的内容 -->
<router-link to="/home/dashboard">控制面板</router-link>
<router-link to="/home/log">日志记录</router-link>
<router-view></router-view>
</div>
</template>

<script>

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

<style scoped>

</style>

在index.js的router中配置

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
import Vue from 'vue'
import VueRouter from 'vue-router'

//懒加载的方式引入组件
const Home = () => import('../components/Home')
const About = () => import('../components/About')

Vue.use(VueRouter)

export default new VueRouter({
routes: [
{path: '',redirect: '/home'},//为根目录进行重定向,默认渲染出home目录下的组件
{
path: '/home',
name: 'home',
component: Home,
//在所被嵌套的路由对象中添加children成员变量
children: [
//注意path前不加斜杠,会默认在父route后拼接/,即最终'/home/dashboard'
{path: 'dashboard', component: ()=>import('../components/DashBoard')},
{path: 'log', component: ()=>import('../components/Log')},
// 为/home默认渲染/home/dashboard
{path: '',redirect: 'dashboard'},
]
},
{
path: '/about',
name: 'about',
component: About //可替代为:() => import('../components/About')
}
],
mode: 'history'
})

3.3参数传递

参数传递的方式:

  • params:对应restful风格URL获取参数,例如/user/:id可通过$route.params.id访问,这点在2.7动态路由中已经展示。

  • query:对应传统Get方式获取请求参数,例如/user?id=1

    可通过v-bind为route-link中的to属性绑定一个对象{path:’url-path’, query: {name: Jelly, age: 18} }

    之后可以适应$route.query.name访问

    最新的vue-router不使用$route.query,而使用耦合度更低的props

现就query方式的编写主要代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<!-- app.vue中使用路由 -->
<router-link v-bind:to="{path:'/profile', query:{name:'Jelly' ,age: 18}}">我的信息</router-link>
<router-view></router-view>

<!-- index.js配置路由 -->
<script>
routes: [
{
path: '/profile',
name: 'profile',
component: ()=> import('../components/Profile')
}
</script>

<!-- prifile.vue -->
<template>
<div>
<h1>我的信息</h1>
<div>{{$route.query.name}}</div>
<div>{{$route.query.age}}</div>
</div>
</template>

于2.5类似的,也可使用javascript的方式进行配置路由

在对应的methods中的点击监听函数写入:

this.$router.push({path: 'profile'}, query: {name:Jelly,age:18} })即可

在使用了 vue-router 的应用中,路由对象会被注入每个组件中,赋值为 this.$route ,并且当路由切换时,路由对象会被更新。

3.4导航守卫

vue-router提供的导航守卫主要用来监听监听路由的进入和离开的。vue-router提供了beforeEach和afterEach等的钩子函数, 它们会在路由即将改变前和改变后触发。简言之,就是在路由的改变过程中添加的回调函数,
供开发者自行实现。

守卫中的this.name

3.4.1全局守卫

全局前置守卫:

1
2
3
4
5
6
router.beforeEach((to,from,next)=>{
alert("全局前置守卫:从"+from.path+"跳转到"+to.path)
//beforeEach只是其中一个钩子,表示继续进行管道中的下一个钩子、
//如果全部钩子执行完了,则导航的状态就是 confirmed (确认的)。
next()
})

全局解析守卫:(实际上应该叫解析前守卫,因为是在目标组件解析前调用的)

在 2.5.0+ 你可以用 router.beforeResolve 注册一个全局守卫。这和 router.beforeEach 类似,区别是在导航被确认之前,同时在所有组件内守卫和异步路由组件被解析之后(此时还没解析需要跳转的目标组件),解析守卫就被调用。具体所有钩子的调用顺序在后文中会详细提及。

1
2
3
4
router.beforeResolve(((to, from, next) => {
alert("全局解析守卫:从"+from.path+"跳转到"+to.path)
next()
}))

全局后置守卫:

1
2
3
4
//不同的是,后置守卫的next函数会被自动调用
router.afterEach((to, from) => {
alert("全局后置守卫:从"+from.path+"跳转到"+to.path)
})

3.4.2局部守卫

路由独享的守卫:

1
2
3
4
5
6
7
8
9
10
//在某个单独的路由配置,表示只有在进入这个路由前才会执行
{
path: '/about',
name: 'about',
component: About, //可替代为:() => import('../components/About')
beforeEnter: (to, from, next) => {
alert("路由/about的独享守卫"+"从"+from.path+"到"+to.path)
next()
}
},

组件内的守卫:
可以在路由组件内直接定义以下路由导航守卫:

beforeRouteEnter
beforeRouteUpdate (2.2 新增)
beforeRouteLeave

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
//以路由/About为例
<template>
<div>
<h1>关于</h1>
<div>这是关于我的相关信息</div>
</div>
</template>

<script>
export default {
name: "About",
/*
beforeRouteEnter 守卫 不能 访问 this,因为守卫在导航确认前被调用,因此即将登场的新组件还没被创建。
不过,你可以通过传一个回调给 next来访问组件实例。在导航被确认的时候执行回调,并且把组件实例作为回调方法的参数。
next(vm => {
// 通过 `vm` 访问组件实例
})
*/
beforeRouteEnter (to, from, next) {
alert("组件内的守卫:进入"+to.path+'前')
// 在渲染该组件的对应路由被 confirm 前调用
// 不!能!获取组件实例 `this`
// 因为当守卫执行前,组件实例还没被创建
next()
},
beforeRouteUpdate (to, from, next) {
//此处并无演示

// 在当前路由改变,但是该组件被复用时调用
// 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
// 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
// 可以访问组件实例 `this`
next()
},
/*这个离开守卫通常用来禁止用户在还未保存修改前突然离开。该导航可以通过 next(false) 来取消。*/
beforeRouteLeave (to, from, next) {

alert("组件内的守卫:离开"+from.path+'前')
// 导航离开该组件的对应路由时调用
// 可以访问组件实例 `this`
next()
}
}
</script>

<style scoped>

</style>

3.3守卫执行顺序

下方实例:

1.访问根目录,执行全局前置守卫

image-20201125165833848

2.渲染其它异步组件(此处是个人理解),然后执行全局解析守卫

image-20201125170031716

3.执行全局后置守卫

image-20201125170047762

4.解析渲染目标组件

image-20201125170247879

5.紧接着点击访问About路由,查看其它守卫的执行顺序

image-20201125170843785

image-20201125170853173

image-20201125170900301

image-20201125170906096

image-20201125170912733

image-20201125171011444

由此即可大致看出,导航守卫的大致执行过程;此处官网详细的执行顺序;

  1. 导航被触发。
  2. 在失活的组件里调用 beforeRouteLeave 守卫。
  3. 调用全局的 beforeEach 守卫。
  4. 在重用的组件里调用 beforeRouteUpdate 守卫 (2.2+)。
  5. 在路由配置里调用 beforeEnter
  6. 解析异步路由组件。
  7. 在被激活的组件里调用 beforeRouteEnter
  8. 调用全局的 beforeResolve 守卫 (2.5+)。
  9. 导航被确认。
  10. 调用全局的 afterEach 钩子。
  11. 触发 DOM 更新。
  12. 调用 beforeRouteEnter 守卫中传给 next 的回调函数,创建好的组件实例会作为回调函数的参数传入。

关系导航守卫的更详细信息

3.5keep-alive

keep-alive 是 Vue 内置的一个组件,可以使被包含的组件保留状态,或避免重新渲染。

这就牵扯到了Vue实例的生命周期,created()函数 和 destroyed()函数,

当一个组件创建的时候,会调用created()函数,当一个组件消亡的时候,会调用 destroyed()函数。

当我们使用 的时候,当我们来回切换组件时,组件并不会被destroy,它会被缓存下来,以备下次调用。

当一个组件处于 <keep-alive> 的状态的时候,它就可以使用 activated()deactivated() 这两个函数,表示组件被激活或失活时的钩子函数

keep-alive 有两个非常重要的属性。

include : 字符串或正则表达,只有匹配的组件会被缓存。

exclude : 字符串或正则表达式,任何匹配的组件都不会被缓存。