Vue-Router
【Vue-Router】
相关理解
vue-router 的理解
vue
的一个插件库,专门用来实现SPA应用
对SPA应用的理解
1.单页Web应用(single page web application,SPA)
2.整个应用只有一个完整的页面
3.点击页面中的导航链接不会刷新页面,只会做页面的局部更新
4.数据需要通过ajax请求获取
路由的理解
1.什么是路由?
一个路由就是一组映射关系(key - value)
key
为路径,value
可能是function
或component
2.路由分类
后端路由
理解:value是function,用于处理客户端提交的请求
工作过程:服务器接收到一个请求时,根据请求路径找到匹配的函数来处理请求,返回响应数据
前端路由
理解:value是component,用于展示页面内容
工作过程:当浏览器的路径改变时,对应的组件就会显示
① 用户点击了页面上的路由链接(本质是a链接)
② 导致了 URL 地址栏中的 Hash 值发生了变化
③ 前端路由监听了到 Hash 地址的变化
④ 前端路由把当前 Hash 地址对应的组件渲染都浏览器中
结论:前端路由,指的是 Hash 地址与组件之间的对应关系!
基本路由
基本使用
-
安装
vue-router
,命令:npm i vue-router@3
-
应用插件:
Vue.use(VueRouter)
-
编写router配置项:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22//引入VueRouter
import VueRouter from 'vue-router'
//引入Luyou 组件
import About from '../components/About'
import Home from '../components/Home'
//创建router实例对象,去管理一组一组的路由规则
const router = new VueRouter({
routes:[
{
path:'/about',
component:About
},
{
path:'/home',
component:Home
}
]
})
//暴露router
export default router -
实现切换
<router-link></router-link>
浏览器会被替换为a标签
active-class
设置 链接激活时使用的 CSS 类名1
<router-link active-class="active" to="/about">About</router-link>
-
指定展示位置
1
<router-view></router-view>
切换的时候会把路由销毁,触发销毁生命周期函数
实际使用
src/router/index.js
该文件专门用于创建整个应用的路由器
1 | import VueRouter from 'vue-router' |
src/main.js
1 | import Vue from 'vue' |
src/App.vue
1 | <template> |
src/components/Home.vue
1 | <template> |
src/components/About.vue
1 | <template> |
几个注意点
- 路由组件通常存放在
pages
文件夹,一般组件通常存放在components
文件夹。 - 通过切换,“隐藏”了的路由组件,默认是被销毁掉的,需要的时候再去挂载。
- 每个组件都有自己的
$route(路由)
属性,里面存储着自己的路由信息。 - 整个应用只有一个router,可以通过组件的
$router(路由器)
属性获取到。
多级路由
-
配置路由规则,使用children配置项:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20routes:[
{
path:'/about',
component:About,
},
{
path:'/home',
component:Home,
children:[ //通过children配置子级路由
{
path:'news', //此处一定不要写:/news
component:News
},
{
path:'message',//此处一定不要写:/message
component:Message
}
]
}
] -
跳转(要写完整路径):
1
<router-link to="/home/news">News</router-link>
-
指定展示位置
1
<router-view></router-view>
src/pages/Home.vue
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20<template>
<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>
</template>
<script>
export default {
name:'Home',
}
</script>src/pages/Message.vue
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19<template>
<ul>
<li>
<a href="/message1">message001</a>
</li>
<li>
<a href="/message2">message002</a>
</li>
<li>
<a href="/message/3">message003</a>
</li>
</ul>
</template>
<script>
export default {
name:'News'
}
</script>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
25
26
27
28
29
30
31//该文件专门用于创建整个应用的路由器
import VueRouter from "vue-router";
//引入组件
import Home from '../pages/Home'
import About from '../pages/About'
import News from '../pages/News'
import Message from '../pages/Message'
//创建并暴露一个路由器
export default new VueRouter({
routes:[
{
path:'/about',
component:About
},
{
path:'/home',
component:Home,
children:[
{
path:'news',
component:News
},
{
path:'message',
component:Message
}
]
}
]
})
vue中路由重定向redirect
5.1 重定向到平级的路径上去
第一个对象里是配置路由path:’/'
为项目的根目录,redirect重定向为渲染的路径redirect:'/index'
(这里我是指向了第二个对象里的path),所以就要写第二个对象方便 redirect 找到它。
重定向的地址不需要接收参数,把"/“重定向到”/index"
重定向到子路由路径上面去
父路由(path:‘/’)重定向到相应的子路由路径上去了 redirect:‘/index’
命名路由
-
作用:可以简化路由的跳转。
-
如何使用
-
给路由命名:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17{
path:'/demo',
component:Demo,
children:[
{
path:'test',
component:Test,
children:[
{
name:'hello' //给路由命名
path:'welcome',
component:Hello,
}
]
}
]
} -
简化跳转:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16<!--简化前,需要写完整的路径 -->
<router-link to="/demo/test/welcome">跳转</router-link>
<!--简化后,直接通过名字跳转 -->
<router-link :to="{name:'hello'}">跳转</router-link>
<!--简化写法配合传递参数 -->
<router-link
:to="{
name:'hello',
query:{
id:666,
title:'你好'
}
}"
>跳转</router-link>
-
路由的query参数
-
传递参数
1
2
3
4
5
6
7
8
9
10
11
12
13<!-- 跳转并携带query参数,to的字符串写法 (先转成js表达式,然后使用模版字符串)-->
<router-link :to="`/home/message/detail?id=666&title=你好`">跳转</router-link>
<!-- 跳转并携带query参数,to的对象写法 -->
<router-link
:to="{
path:'/home/message/detail',
query:{
id:666,
title:'你好'
}
}"
>跳转</router-link> -
接收参数:
1
2$route.query.id
$route.query.title
路由的params参数
基本使用
-
配置路由,声明接收
params
参数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
},
{
component:Message,
children:[
{
name:'xiangqing',
path:'detail/:id/:title', //使用占位符声明接收params参数
component:Detail
}
]
}
]
} -
传递参数
1
2
3
4
5
6
7
8
9
10
11
12
13<!-- 跳转并携带params参数,to的字符串写法 -->
<router-link :to="/home/message/detail/666/你好">跳转</router-link>
<!-- 跳转并携带params参数,to的对象写法 -->
<router-link
:to="{
name:'xiangqing',
params:{
id:666,
title:'你好'
}
}"
>跳转</router-link>特别注意:路由携带params参数时,若使用to的对象写法,则不能使用path配置项,必须使用name配置!
-
接收参数:
1
2$route.params.id
$route.params.title
params传参问题
(1)如何指定params参数可传可不传
如果路由path要求传递params参数,但是没有传递,会发现地址栏URL有问题,详情如下:
1 | Search路由项的path已经指定要传一个keyword的params参数,如下所示: |
解决方法
可以通过改变path来指定params参数可传可不传
path: "/search/:keyword?"
,?
表示该参数可传可不传
(2)由(1)可知params可传可不传,但是如果传递的是空串,如何解决 。
1 | this.$router.push({name:"Search",query:{keyword:this.keyword},params:{keyword:''}}) |
解决方法
加入
||undefined
,当我们传递的参数为空串时地址栏url也可以保持正常
this.$router.push({name:"Search",params:{keyword:this.keyword||undefined}})
路由的props配置
作用:让路由组件更方便的收到参数
1 | { |
src/pages/Detail.vue
1 | <template> |
replace
- 作用:控制路由跳转时操作浏览器历史记录的模式
- 浏览器的历史记录有两种写入方式:分别为
push
和replace
,push
是追加历史记录,replace
是替换当前记录。路由跳转时候默认为push
- 如何开启
replace
模式:<router-link replace .......>News</router-link>
总结:浏览记录本质是一个栈,默认push
,点开新页面就会在栈顶追加一个地址,后退,栈顶指针向下移动,改为replace
就是不追加,而将栈顶地址替换
src/pages/Home.vue
1 | <template> |
编程式路由导航
基本使用
作用:不借助<router-link>
实现路由跳转,让路由跳转更加灵活
this.$router.push({})
内传的对象与<router-link>
中的to相同,跳转到指定 hash 地址,并增加一条历史记录
this.$router.replace({})
跳转到指定的 hash 地址,并替换掉当前的历史记录
this.$router.forward()
前进
this.$router.back()
后退
this.$router.go(n)
可前进也可后退,n为正数前进n,为负数后退
src/pages/Message.vue
1 | <template> |
src/components/Banner.vue
1 | <template> |
简化写法
1 | <button @click="$router.forward">前进</button> |
多次执行相同的push问题
多次执行相同的push问题,控制台会出现警告
例如:使用this.$router.push({name:‘Search’,params:{keyword:“…”||undefined}})时,如果多次执行相同的push,控制台会出现警告。
1 | let result = this.$router.push({name:"Search",query:{keyword:this.keyword}}) |
执行一次上面代码:
多次执行出现警告:
原因:push是一个promise,promise需要传递成功和失败两个参数,我们的push中没有传递。
方法:this.$router.push({name:'Search',params:{keyword:".."||undefined}},()=>{},()=>{})
后面两项分别代表执行成功和失败的回调函数。
这种写法治标不治本,将来在别的组件中push|replace,编程式导航还是会有类似错误
push是VueRouter.prototype的一个方法,在router中的index重写该方法即可(看不懂也没关系,这是前端面试题)
1 | //1、先把VueRouter原型对象的push,保存一份 |
缓存路由组件
-
作用:让不展示的路由组件保持挂载,不被销毁。
-
具体编码:
这个 include 指的是组件名
1
2
3
4
5
6
7
8
9// 缓存一个路由组件
<keep-alive include="News"> // include中写想要缓存的组件名,不写表示全部缓存
<router-view></router-view>
</keep-alive>
// 缓存多个路由组件
<keep-alive :include="['News','Message']">
<router-view></router-view>
</keep-alive>
src/pages/News.vue
1 | <template> |
切换完回来不会清楚已输入的内容
两个新的生命周期钩子
- 作用:路由组件所独有的两个钩子,用于捕获路由组件的激活状态。
- 具体名字:
activated
路由组件被激活时触发。deactivated
路由组件失活时触发。
因为这个路由使用keep-alive,所以切换不会清除定时器
src/pages/News.vue
1 | <template> |
路由守卫
- 作用:对路由进行权限控制
- 分类:全局守卫、独享守卫、组件内守卫
路由守卫总共有7个
全局路由守卫:
beforeEach 前置守卫
affterEach 后置守卫
beforeResolve 解析守卫
路由的守卫
beforeRouterEnter 进入组件之前触发,在Created前面
beforeRouterUpdated 路由更新但是内容不会改变
beforeRouterLeave 离开之前触发,在beforeDestory之前触发
路由独享守卫
beforeEnter 读取路由的信息
全局路由守卫
每次发生路由的导航跳转时,都会触发全局前置守卫。因此,在全局前置守卫中,程序员可以对每个路由进行访问权限的控制:
你可以使用 router.beforeEach 注册一个全局前置守卫:
1 | //创建路由的实例对象 |
每个守卫方法接收三个参数:
to: Route: 即将要进入的目标路由对象
from: Route: 当前导航正要离开的路由
next: Function: 钩子函数,里面定义参数,确认下一步路由要做什么
next(’/’)或者 next({ path: ‘/’ }): 跳转到一个不同的地址。当前的导航被中断,然后进行一个新的导航。你可以向 next 传递任意位置对象,next({name: ‘home’}) 。
next 函数的 3 种调用方式
当前用户拥有后台主页的访问权限,直接放行:next()
当前用户没有后台主页的访问权限,强制其跳转到登录页面:next(‘/login’)
当前用户没有后台主页的访问权限,不允许跳转到后台主页:next(false)
一般应用在用户未能验证身份时重定向到 /login :
1 | router.beforeEach((to, from, next) => { |
例
1 | //该文件专门用于创建整个应用的路由器 |
独享路由守卫
你可以在路由配置上直接定义 beforeEnter 守卫:
1 | const router = new VueRouter({ |
这些守卫与全局前置守卫的方法参数是一样的。
例
1 | //该文件专门用于创建整个应用的路由器 |
组件内路由守卫
可以在路由组件内直接定义以下路由导航守卫:
进组组件前的守卫 beforeRouteEnter
路由更新时的守卫 beforeRouteUpdate (2.2 新增)
离开组件时的守卫 beforeRouteLeave
1 | beforeRouteEnter(to, from, next) { |
beforeRouteEnter
守卫 不能 访问 this,因为守卫在导航确认前被调用,因此即将登场的新组件还没被创建。不过,你可以通过传一个回调给 next来访问组件实例。在导航被确认的时候执行回调,并且把组件实例作为回调方法的参数。
1 | beforeRouteEnter (to, from, next) { |
注意 beforeRouteEnter 是支持给 next传递回调的唯一守卫。对于 beforeRouteUpdate和beforeRouteLeave 来说,this 已经可用了,所以不支持传递回调,因为没有必要了。
1 | beforeRouteUpdate (to, from, next) { |
这个离开守卫通常用来禁止用户在还未保存修改前突然离开。该导航可以通过 next(false) 来取消。
1 | beforeRouteLeave (to, from, next) { |
总结
完整的导航解析流程
导航被触发。
在失活的组件里调用beforeRouteLeave
守卫。
调用全局的 beforeEach
守卫。
在重用的组件里调用beforeRouteUpdate
守卫 (2.2+)。
在路由配置里调用 beforeEnter
。
解析异步路由组件。
在被激活的组件里调用 beforeRouteEnter
。
调用全局的 beforeResolve
守卫 (2.5+)。
导航被确认。
调用全局的 afterEach
钩子。
触发 DOM 更新。
调用 beforeRouteEnter
守卫中传给 next 的回调函数,创建好的组件实例会作为回调函数的参数传入。
路由懒加载
当打包构建应用时,JavaScript 包会变得非常大,影响页面加载。如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就更加高效了。
1 | { |
新的导入方式,这样导入在加载时只会按需加载
路由器的两种工作模式
-
对于一个url来说,什么是hash值?—— #及其后面的内容就是hash值。
-
hash值不会包含在 HTTP 请求中,即:hash值不会带给服务器。
-
hash模式:
- 地址中永远带着#号,不美观 。
- 若以后将地址通过第三方手机app分享,若app校验严格,则地址会被标记为不合法。
- 兼容性较好。
-
history模式:
- 地址干净,美观 。
- 兼容性和hash模式相比略差。
- 应用部署上线时需要后端人员支持,解决刷新页面服务端404的问题。
监听路由
复用组件时,想对路由参数的变化作出响应的话,你可以简单地 watch(监测变化) $route 对象:
最佳方法:我们每次进行新的搜索时,我们的query和params参数中的部分内容肯定会改变,而且这两个参数是路由的属性。我们可以通过监听路由信息的变化来动态发起搜索请求。
如下图所示,$route是组件的属性,所以watch是可以监听的(watch可以监听组件data中所有的属性)
注意:组件中data的属性包括:自己定义的、系统自带的(如 $route)、父组件向子组件传递的等等
1 | watch:{ |
滚动行为
使用前端路由,当切换到新路由时,想要页面滚到顶部,或者是保持原先的滚动位置,就像重新加载页面那样。 vue-router 能做到,而且更好,它让你可以自定义路由切换时页面如何滚动。
注意: 这个功能只在支持 history.pushState 的浏览器中可用。
当创建一个 Router 实例,你可以提供一个 scrollBehavior
方法:
1 | const router = createRouter({ |
scrollBehavior
函数接收 to
和 from
路由对象,如 Navigation Guards。第三个参数 savedPosition
,只有当这是一个 popstate
导航时才可用(由浏览器的后退/前进按钮触发)。
https://router.vuejs.org/zh/guide/advanced/scroll-behavior.html
Vuex 打包 图片懒加载