Vue路由技术
本章节我们学习的重点是Vue路由,路由是vue.js中重要的组成部分,它和 Vue.js 的核心深度集成,让构建单页面应用变得易如反掌。路由实际上就是可以理解为指向,就是我在页面上点击一个按钮需要跳转到对应的页面,这就是路由跳转。
12.1单页面应用
提起路由,读者应该很容易联想起来现实生活中的路由器,路由器是用来共享网络的,可以实现多台设备使用上网,也就是说虽然只有一根网线,也可以将数据分发给网络内的多个设备。路由器的原理如图12.1所示。
图12.1 路由器
那么Vue的路由和路由器有关系吗?这里先介绍单页面应用(SPA)的概念。
1、single-page application是一种特殊的Web应用。它将所有的活动局限于一个Web页面中,仅在该Web页面初始化时加载相应的HTML、JavaScript、CSS。一旦页面加载完成,SPA不会因为用户的操作而进行页面的重新加载或跳转,而是利用JavaScript动态的变换HTML(采用的是div切换显示和隐藏),从而实现UI与用户的交互。
2、简单来说SPA的网页只有一个页面,而这个网页的实际方式要能够回应使用者所使用的各种装置并且赋值使用者在电脑上使用软件的体验,让使用者可以更容易和有效的使用网站。按照正常情况下,我们会在一个页面中链接到其他的很多个页面,进行页面的跳转,但是如果使用单页面应用的话,我们始终在一个页面中,通常使用a标签的描点来实现。
这里我们用一张图片演示一下什么叫做单页面应用,如图12.2所示。
图12.1 单页面应用
单页面的优缺点:
优点:
1、用户操作体验好,用户不用刷新页面,整个交互过程都是通过Ajax来操作。
2、适合前后端分离开发,服务端提供http接口,前端请求http接口获取数据,使用JS进行客户端渲染。
缺点:
1、首页加载慢
单页面应用会将js、 css打包成一个文件,在加载页面显示的时候加载打包文件,如果打包文件较大或者网速慢则用户体验不好
2、 SEO不友好
有这些缺点,为什么还要使用Vue呢?Vue还提供了一些其它的技术来解决这些缺点,比如说服务器端渲染技术(SSR),通过这些技术可以完美解决这些缺点,解决完这些问题,实际上单页面应用对于前端来说是非常完美的页面开发解决方案。
12.2 Vue-router入门
Vue Router 是 Vue.js 官方的路由管理器。它和 Vue.js 的核心深度集成,让构建单页面应用变得易如反掌。路由实际上就是可以理解为指向,就是我在页面上点击一个按钮需要跳转到对应的页面,这就是路由跳转;首先我们来学习三个单词(route,routes,router):
• route:首先它是个单数,译为路由,即我们可以理解为单个路由或者某一个路由;
• routes:它是个复数,表示多个的集合才能为复数;即我们可以理解为多个路由的集合,JS中表示多种不同状态的集合的形式只有数组和对象两种,事实上官方定义routes是一个数组;所以我们记住了,routes表示多个数组的集合;
• router:译为路由器,上面都是路由,这个是路由器,我们可以理解为一个容器包含上述两个或者说它是一个管理者,负责管理上述两个;举个常见的场景的例子:当用户在页面上点击按钮的时候,这个时候router就会去routes中去查找route,就是说路由器会去路由集合中找对应的路由;
12.2.1 搭建Vue应用
我们结合一个小demo来理解和学习路由,首先我们需要安装vue-cli来构建一个vue的开发环境。项目名称为vue_router_demo。安装完vue-cli之后,我们的项目目录结构如图12.3所示。
图12.3 vue应用目录结构
12.2.2 安装vue-router
我们在命令行中输入npm install vue-router来安装vue-router,安装完之后我们可以打开package.json文件,在package.json文件中可以看到vue-router的版本号;如图12.4所示。
图12.4 路由版本
12.2.3 编写代码
完成了Vue-router的安装之后我们在此项目基础上创建文件夹与文件演示路由的使用。我们在src目录下新建router文件夹,并且在router文件夹中创建三个文件,分别为page1.vue和page2.vue以及router.js,如图12.5所示。
图12.5 新建文件
其中page1.vue,page2.vue,page3.vue为三个单页面,router.js作为vue路由功能模块js文件。
page1.vue代码如例12-1所示。
例12-1 page1.vue
1 <template>
2 <div>
3 <h1>page1</h1>
4 <p>{{msg}}</p>
5 </div>
6 </template>
7 <script>
8 export default {
9 data () {
10 return {
11 msg: "我是page1组件"
12 }
13 }
14 }
15 </script>
page2.vue,page3.vue与page1.vue代码雷同只是页面中显示不同文件的文件名,这里不再单独展示page2.vue和page3.vue的代码。
router.js代码如例12-2所示。
例12-2 router.js
1 //引入vue
2 import Vue from 'vue';
3 //引入vue-router
4 import VueRouter from 'vue-router';
5 //第三方库需要use一下才能用
6 Vue.use(VueRouter)
7 //引用page1页面
8 import page1 from './page1.vue';
9 //引用page2页面
10 import page2 from './page2.vue';
11
12 //定义routes路由的集合,数组类型
13 const routes=[
14 //单个路由均为对象类型,path代表的是路径,component代表组件
15 {path:'/page1',component:page1},
16 {path:"/page2",component:page2}
17 ]
18
19 //实例化VueRouter并将routes添加进去
20 const router=new VueRouter({
21 //ES6简写,等于routes:routes
22 routes
23
24 });
25
26 //抛出这个这个实例对象方便外部读取以及访问
27 export default router
接下来我们需要修改一下main.js文件。修改之后的结果如图12.6所示。
图12.6 main.js文件
最后还需要在首页中添加跳转相关代码。app.vue文件修改之后template部分如图12.7所示
图12.6 app.vue文件
<router-link>标签类似html中的a标签,to属性相当于a标签的href属性,表示跳转到的组件路径。<router-view>标签表示页面中显示的部分,也就是在此标签内部显示嵌套的组件。
12.2.3 运行项目
运行项目之后可以看到如下效果,我们的页面就可以进行路由跳转和切换了,路由的基本使用就完成了,router-view页面展示部分负责拿符合条件路由的页面来展示,实际上url是没有发生变化的。运行效果如图12.7所示。
图12.7 运行结果
12.2.4 代码解释
以上项目中路由的定义比较简单,在这里我们进行详细学习,如何定义个一节路由的配置。
//routes:是一个数组,数组成员是一个个对象,包含每个路由的具体配置,详细配置信息可参阅手册
const router = new VueRouter({
routes:[
{
path:String //定义路由路径
name:String //定义命名路由
component:Component //路由所映射的组件,1.直接导入引用 2:路由懒加载
//component:Home,
//conponent:()=>import('./../views/Home.vue')
children:Array //定义嵌套路由,子路由
redirect:String|Location|Function //路由重定向
}
],
mode:"hash" | "history" | "abstract" //定义路由模式
//hash: 使用 URL hash 值来作路由。支持所有浏览器
//history: 依赖 HTML5 History API 和服务器配置
//abstract: 支持所有 JavaScript 运行环境,如 Node.js 服务器端。如果发现没有浏览器的 API,路由会自动强制进入这个模式。
});
在页面定义导航,app.vue详细代码的使用方式如下。
1.在页面定义导航
//使用 router-link 组件来导航
//通过传入 `to` 属性指定链接(目标地址)
//<router-link> 默认会被渲染成一个 `<a>` 标签
<router-link to="/home">home</router-link>
#router-link有很多属性,具体参阅文档
to:目标地址
tag:指定何种标签,同样它还是会监听点击,触发导航。默认a
active-class:设置链接激活时使用的 CSS 类名
exact:精确匹配模式
要注意,当 <router-link> 对应的路由匹配成功,将自动设置 class 属性值 .router-link-active
2.定义路由出口
//路由匹配到的组件将渲染在这里
<router-view></router-view>
12.3 嵌套路由
很多时候我们的页面结构决定了我们可能需要嵌套路由,比如当我们进入主页之后有分类,然后当选择其中一个分类之后进入对应的详情,这个时候我们就可以用到嵌套路由;官方文档中给我们提供了一个children属性,这个属性是一个数组类型,里面实际放着一组路由;这个时候父子关系结构就出来了,所以children属性里面的是路由相对来说是children属性外部路由的子路由。
我们在vue_router_demo项目中继续演示嵌套路由的使用,在我们的src目录下新建两个vue文件,分别是phone.vue和computer.vue。
phone.vue代码如下。
<template>
<div>
<p>{{msg}}</p>
</div>
</template>
<script>
export default {
data () {
return {
msg: "嵌套手机组件"
}
}
}
</script>
computer.vue代码如下。
<template>
<div>
<p>{{msg}}</p>
</div>
</template>
<script>
export default {
data () {
return {
msg: "嵌套电脑组件"
}
}
}
</script>
删除app.vue文件,删除page2的路由跳转,只剩下一个page1,接下来我们将page1配置成phone.vue和computer.vue的上一级,实现嵌套路由。
<template>
<div id="app">
<img src="./assets/logo.png">
<div>
<router-link to="/page1">Page1</router-link>
</div>
<router-view></router-view>
</div>
</template>
接下来的重点是配置router.js中针对路由实例的配置,修改如下。
//定义routes路由的集合,数组类型
const routes=[
//单个路由均为对象类型,path代表的是路径,component代表组件
{ path:'/page1',
component:page1,
children: [
{
path: "phone",
component: phone
},
{
path: "computer",
component: computer
},
]},
]
12.4 动态路由(路由传参)
我们经常需要把某种模式匹配到的所有路由,全都映射到同个组件。例如,我们有一个 User 组件,对于所有 ID 各不相同的用户,都要使用这个组件来渲染。那么,我们可以在 vue-router 的路由路径中使用“动态路径参数”来达到这个效果。
const router = new VueRouter({
routes: [
// 动态路径参数 以冒号开头
{ path: '/user/:id', component: User }
]
})
//现在呢,像 /user/foo 和 /user/bar 都将映射到相同的路由
<router-link to='/user/uid00001'>张三</router-link>
<router-link to='/user/uid00002'>李四</router-link>
//一个“路径参数”使用冒号 : 标记。当匹配到一个路由时,参数值会被设置到 this.$route.params,可以在每个组件内使用。于是,我们可以更新 User 的模板,输出当前用户的 ID:
<div>User {{ $route.params.id }}</div>
在组件中使用 $route 会使之与其对应路由形成高度耦合,从而使组件只能在某些特定的 URL 上使用,限制了其灵活性。
使用 props 将组件和路由解耦:
const User = {
props: ['id'],
template: '<div>User {{ id }}</div>'
}
const router = new VueRouter({
routes: [
{ path: '/user/:id', component: User, props: true }
]
})
12.5 编程式路由
路由导航两种方式,分别是标签导航和编程式导航,目前我们使用的是标签导航,还有一种我们还可以借助 router 的实例方法,通过编写代码来实现叫做编程式导航:
• 标签导航:标签导航<router-link><router-link>是通过转义为<a></a>标签进行跳转,其中router-link标签中的to属性会被转义为a标签中的href属性;
//跳转到名为user路由,并传递参数userId
<router-link :to="{ name: 'user', params: { userId: 123 }}">User</router-link>
• 编程式导航:我们可以通过this.$router.push()这个方法来实现编程式导航,当然也可以实现参数传递,这种编程式导航一般是用于按钮点击之后跳转
router.push({ name: 'user', params: { userId: 123 }})
以上两种方式都会把路由导航到user/123路径。那么我们现在重点学习编程式路由,push只是其中一个方法,除了push方法以外还有replace和go方法,他们之间有所区别。
三个跳转方法的区别:
• push方法
想要导航到不同的 URL,则使用 router.push 方法。这个方法会向 history 栈添加一个新的记录,所以,当用户点击浏览器后退按钮时,则回到之前的 URL,当你点击 <router-link> 时,这个方法会在内部调用,所以说,点击 <router-link :to="..."> 等同于调用 router.push(...)。该方法的参数可以是一个字符串路径,或者一个描述地址的对象,例如。
// 字符串
router.push('home')
// 对象
router.push({ path: 'home' })
// 命名的路由
router.push({ name: 'user', params: { userId: '123' }})
// 带查询参数,变成 /register?plan=private
router.push({ path: 'register', query: { plan: 'private' }})
• replace方法
跟 router.push 很像,唯一的不同就是,它不会向 history 添加新记录,而是跟它的方法名一样替换掉当前的 history 记录。相当于标签导航<router-link :to="..." replace>。
• go方法
这个方法的参数是一个整数,意思是在 history 记录中向前或者后退多少步,类似 window.history.go(n)。使用方法如下。
// 在浏览器记录中前进一步,等同于 history.forward()
router.go(1)
// 后退一步记录,等同于 history.back()
router.go(-1)
// 前进 3 步记录
router.go(3)
// 如果 history 记录不够用,那就默默地失败呗
router.go(-100)
router.go(100)
12.6 别名的使用
有的时候,通过一个名称来标识一个路由显得更方便一些,所以官方为了方便我们偷懒,又给我们在路由中添加了一个name属性,命名这个属性之后我们访问这个属性就等于直接访问到路由;其实两者并没有什么区别,只是提供了两种方式来访问路由,可以通过路径来匹配也可以通过别名来匹配;比如针对以下路由的两种路由书写方式。
const router = new VueRouter({
routes: [
{
path: '/user/:userId',
name: 'user',
component: User
}
]
})
普通路由:
<router-link :to="{ name: 'user', params: { userId: 123 }}">User</router-link>
别名路由:
router.push({ name: 'user', params: { userId: 123 }})
12.7 重定向
“重定向”的意思是,当用户访问 /a时,URL 将会被替换成 /b,然后匹配路由为 /b,重定向也是通过 routes 配置来完成,下面例子是从 /a 重定向到 /b:
const router = new VueRouter({
routes: [
{ path: '/a', redirect: '/b' },
// or
{ path: '/a', redirect: { name: 'foo' }}
]
})
12.8 路由的过渡动画
在页面切换时我们加入一些动画效果,可以更高端大气上档次,提升我们程序的动效设计。这小结内容就是路由的过渡动画效果的制作。动画的制作主要靠标签<transition>已经css过度名来实现。
首先我们来介绍一个transition标签的使用。想让路由有过渡动画,需要在标签的外部添加标签,标签还需要一个name属性。在/src/App.vue文件里添加了<transition>标签,包裹路由出口标签,并给标签起一个名字叫fade,fade为自定义,这样就表示经router-view进行切换的单页面在进行切换时想要加入过度动画效果。
<transition name="fade">
<router-view/>
</transition>
那具体是什么样的过度动画效果呢?这里需要使用到css动画,css的四个过渡类名:
• fade-enter:进入过渡的开始状态,元素被插入时生效,只应用一帧后立刻删除;
• fade-enter-active:进入过渡的结束状态,元素被插入时就生效,在过渡过程完成后移除;
• fade-leave:离开过渡的开始状态,元素被删除时触发,只应用一帧后立刻删除;
• fade-leave-active:离开过渡的结束状态,元素被删除时生效,离开过渡完成后被删除。
那我们就在App.vue页面里加入四种CSS样式效果,并利用CSS3的transition属性控制动画的具体效果,在App.vue中style部分添加入下代码。
.fade-enter { //一瞬间不加冒号
opacity:0; //做一个淡入淡出的效果,设置为0,代表透明度
}
.fade-leave{
opacity:1; //不透明
}
.fade-enter-active{
transition:opacity .5s; //做浏览器兼容要去学习o、ms等等前缀
}
.fade-leave-active{
opacity:0;
transition:opacity .5s;
}
上边的代码设置了改变透明度的动画过渡效果,接下来就可以在transition标签找那个指定name为fade,还需要指定mode模式,mode用于配置当前组件和切换新组件两者之间谁先开始执行动画,默认的mode模式in-out模式。
过渡模式mode的可选值:
• in-out:新元素先进入过渡,完成之后当前元素过渡离开;
• out-in:当前元素先进行过渡离开,离开完成后新元素过渡进入。
12.9 mode的设置和404页面的处理
12.9.1 mode的设置
在学习过渡效果的时候,我们学了mode的设置,本小节学习的mode非此设置,而是在路由的属性中还有一个mode。本章节我们就学习路由属性中的mode模式和404页面的设置。
首先看一下mode的使用位置以及写法,在路由的配置文件index.js中,routes:{}中放入mode。
export default new Router({
mode:'history',
routes: [...]
})
mode的两个值:
• history:当你使用history模式时,URL 就像正常的 url,例如 http://daisy.com/lms/,没有之前的#号;
• hash:默认’hash’值,就像我们之前的#号。但是hash看起来就像无意义的字符排列,不太好看也不符合我们一般的网址浏览习惯。
12.9.2 404页面
学习web的开发者对404页面都再熟悉不过了,在路径不存在时我们需要给用于一个友好的提示,为此美工都会设计一个漂亮的页面,这个页面就是我们常说的404页面。vue-router也为我们提供了这样的机制。我们直接来看一下在路由中如何配置404页面。
可以在/src/components/下先创建error.vue文件作为404错误提示页面,这里主要进行步骤的讲解,并没有进行页面的美化,error.vue代码如下。
<template>
<h2>{{msg}}</h2>
</template>
<script>
export default {
data(){
return{
msg:'Error:404'
}
}
}
</script>
接下来打开/src/router/router.js文件,在这里对需要对路由器进行如下配置。
//引入错误页面组件
import Error from '@/components/Error'
//path:'*'就是找不到页面时的配置,component就是我们新建的一个Error.vue文件
{
path:'*',
component:Error
}
至此我们的404提示页面的配置已经完成,运行项目,浏览器地址栏中随意输入当前应用下不存在的路径就会跳转到404页面。
12.10 路由守卫
何为路由守卫?路由守卫有点类似于ajax的请求拦截器,就是请求发送之前先给你拦截住做一些事情之后再去发送请求,同样这里的路由守卫意思差不多;简单理解为就是你在进路由之前,首先把你拦住,对你进行检查;这是不是有点中学门口的保安?进来之前拦住,有学生证就进,没有学生证就不让进;当然,路由守卫不仅仅只是在你进入之前拦住你,还有其他的钩子函数进行其他操作;
vue-router一共给我们提供了三大类钩子函数来实现路由守卫:
1、全局钩子函数(beforeEach、afterEach)
2、路由独享的钩子函数(beforeEnter)
3、组件内钩子函数(beforeRouterEnter、beforeRouterUpdate、beforeRouterLeave)
12.10.1 全局钩子函数
全局钩子函数包含beforeEach和afterEach,针对所有的路由生效。
beforeEach一共接收三个参数,分别是to、from、next;to:即将进入的路由对象;from:正要离开的路由对象;next:路由的控制参数;
next一共有四种调用方式:
next():一切正常调用这个方法进入下一个钩子;
next(false):取消路由导航,这时的url显示的是正要离开的路由地址;
next('/login'):当前路由被终止,进入一个新的路由导航(路由地址可以自由指定)
next(error):路由导航终止并且错误会被传递到router.onError()注册过的回调中;
我们一般是用全局钩子来控制权限,像什么进页面没有登录就跳登录页,需要用户达到什么级别才能访问当前页面都是属于页面权限控制,都是可以通过beforeEach钩子函数来实现。
main.js(全局钩子函数我们一般是在main.js中进行书写):
// 进入路由前方法勾子
router.beforeEach((to, from, next) => {
console.log(to, '前置第一个参数')
console.log(from, '前置第二个参数')
console.log(next, '前置第三个参数')
/
to 目标路由
from 源路由
next 跳转到下一个路由
*/
//这里暂时用local、storange来简单模拟验证权限
if (window.localstorange.getItem("token")) {
// 如果存在,则直接跳转到对应路由
next();
} else {
// 如果不存在,则跳转到登录页
next('/login');
}
});
AfterEach和beforeEach一样都是属于全局守卫钩子,都是在main.js中进行调用;其中AfterEach比beforeEach少一个next参数;假如我们有一个页面很长,滚动后其中的某个位置后跳转,这时新的页面的滚动条位置就会在上一个页面停留的位置;这个时候我们就可以利用afterEach进行重置。
//全局路由改变后钩子
router.afterEach((to, from) => {
//将滚动条恢复到最顶端
window.scrollTo(0, 0);
})
12.10.2 路由独享的钩子函数
beforeEneter:路由独享顾名思义就是指定的路由才有这些钩子函数,通常这类路由独享的钩子函数我们是在路由配置文件中进行配置,只能设置改变前的钩子,不能设置改变后的钩子。
#可以路由配置上直接定义 beforeEnter 守卫:
{
path:'/about',
name:'about',
beforeEnter(to,from,next){
}
}
#上述代码理解为只有进入/about才会触发beforeEnter这个钩子,如果进入其他页面,是不触发的。
12.10.2 组件内的钩子函数
组件内的钩子函数包含beforeRouteEnter,beforeRouteUpdate,beforeRouteLeave。
beforeRouteEnter。
beforeRouteEnter (to, from, next) {
// 在渲染该组件的对应路由被 confirm 前调用
// 不!能!获取组件实例 `this`
// 因为当守卫执行前,组件实例还没被创建
},
beforeRouteUpdate (to, from, next) {
// 在当前路由改变,但是该组件被复用时调用
// 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
// 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
// 可以访问组件实例 `this`
},
beforeRouteLeave (to, from, next) {
// 导航离开该组件的对应路由时调用
// 可以访问组件实例 `this`
}
12.11 本章小结
• single-page application是一种特殊的Web应用。它将所有的活动局限于一个Web页面中,仅在该Web页面初始化时加载相应的HTML、JavaScript、CSS。一旦页面加载完成,SPA不会因为用户的操作而进行页面的重新加载或跳转,而是利用JavaScript动态的变换HTML(采用的是div切换显示和隐藏),从而实现UI与用户的交互
• route译为路由,即我们可以理解为单个路由或者某一个路由。
• routes表示多个数组的集合。
• router译为路由器,可以理解为一个容器包含route和routes两个,说它是一个管理者。
• 路由导航两种方式,分别是标签导航和编程式导航。
• 路由可以通过路径来匹配也可以通过别名来匹配。
• 路由守卫有点类似于ajax的请求拦截器,就是请求发送之前先给你拦截住做一些事情之后再去发送请求。
• 全局钩子函数包含beforeEach和afterEach,针对所有的路由生效。
12.12 理论习题与实践练习
1.思考题
1.1 请简述什么叫做单页面应用。
1.2 请简述单页面应用的优缺点。
1.3 请简述vue-router使用步骤。
1.4 请简述router实例如何配置?
1.5 请分别列举编程式路由和标签式路由的对应。
1.6 请简述路由守卫的分类,以及每种分离包含的钩子函数。
2.编程题
• 2.1. 电影页 改路由
• 2.2. 作业-多级路由
• 2.3. 作业-路由传参
•
- 点赞
- 收藏
- 关注作者
评论(0)