本文目录:
- 1.什么是前端路由
- 2.vue-router使用步骤
- 3.什么是单页应用
- 4.配置路由
- 5.使用router-link实现跳转
- 6.嵌套路由
- 7.命名路由
- 8.命名视图
- 9.重定向
- 10.别名
- 11.编程式导航
- 12.动态路由匹配
- 13.路由组件传参
- 14.记录滚动位置
- 15.vue-router中的全局钩子函数
- 16.vue-router中写到路由记录里的钩子函数
- 17.写在组件内部的路由钩子函数
- 18.路由切换的动画效果
1.什么是前端路由
路由这个概念最先是后端出现的,发送不同的请求,后端根据请求的不同返回不同的资源,这个时候的url是和后端交互的,需要在后端去配置路由的跳转,这种开发方式有两个特点,一是整个项目中前端代码和后端代码是柔在一起的,通常都是需要模版引擎来支持,代码杂糅在一起后不方便本地开发调试,一旦后端代码有错误,前端无法进行开发,前端被限制在后端的开发方式中,效率很低,项目通常是后端主导,二是每次切换一个页面都需要去重新请求服务器,即使两个页面有很多相似的地方都需要去重新请求,随着单页应用的发扬光大,MVVM概念兴起,前后端分离以及前端工程化的发展,前端所做的事情越来越多,前端圈快速崛起。这里说的单页应用,通俗的说就是一个页面,是无刷新的,url变化,对应的是组件的切换,前端路由是为了实现这种单页应用而出现的。总结起来就是前端路由就是在前端去控制不同url路径,切换不同的组件,可以认为url和组件是一种映射关系
2.vue-router使用步骤
1.安装模块
npm install vue-router --save
2.引入模块(在router文件夹中的index.js中引入,先import Vue from 'vue',然后)
import VueRouter from 'vue-router'
3.作为Vue的插件
Vue.use(VueRouter)
4.创建路由实例对象
const router = new VueRouter({
routes: [
{
path: '/',
component: Home
},
{
path: '/questions',
component: Questions
},
{
path: '/vip',
component: Vip
},
{
path: '/course',
component: Course
}
]
})
//对router进行导出
export default router
5.注入Vue选项参数(在main.js中进行引入)
import router from './router'
new Vue({
router
})
6.告诉路由渲染的位置
匹配上的组件会被渲染到router-view这个位置
<router-view></router-view>
7.放置跳转标签
路由最开始默认就有一个#号
<a href = '#/'></a>
<a href = '#/question'></a>
<a href = '#/vip'></a>
<a href = '#/course'></a>
通过上面的步骤,当我们点击a标签的时候,被匹配到的路由所映射的组件就会被渲染到<router-view>中
注意:上面步骤的讲解主要是为了方便理解,实际应用中的vue项目通常由脚手架工具搭建,搭建的时候勾选上router,项目中就默认配置安装好了vue-router
3.什么是单页应用
百度百科解释,单页Web应用(single page web application,SPA),就是只有一张Web页面的应用,是加载单个HTML 页面并在用户与应用程序交互时动态更新该页面的Web应用程序。
4.配置路由
需求:点击不同的菜单(首页、课程、会员、问答),下方显示不同的文字
首页:App组件代码
<template>
<div id="app">
<div class='page'>
<ul>
<li><a href="#/">首页</a></li>
<li><a href="#/course">课程</a></li>
<li><a href="#/vip">会员</a></li>
<li><a href="#/question">问答</a></li>
</ul>
</div>
<router-view></router-view>
</div>
</template>
<script>
export default {
name: 'App',
components: {
}
}
</script>
<style scoped>
</style>
路由配置:roiter文件夹中的index.js
import Vue from 'vue'
import VueRouter from 'vue-router'
//下面是引入的四个路由
import Home from '@/components/Home'
import Course from '@/components/Course'
import Question from '@/components/Question'
import Vip from '@/components/Vip'
// 让vue-router作为vue的插件使用
Vue.use(VueRouter)
// 配置路由信息
const router = new VueRouter({
routes: [
{
path: '/',
component: Home
},
{
path: '/course',
component: Course
},
{
path: '/vip',
component: Vip
},
{
path: '/question',
component: Question
}
]
})
export default router
上面的代码就可以通过点击实现路由的跳转了,但是a标签的路由会在路径前面添加一个"#",显得特别的不优雅,我们可以通过router-link进行优化
5.使用router-link实现跳转
首页:App组件代码
<li><router-link to="/">首页</router-link></li>
<li><router-link to="/course">课程</router-link></li>
<li><router-link to="/vip">会员</router-link></li>
<li><router-link to="/question">问答</router-link></li>
router-link的其他配置
1.可以动态绑定一个变量
<li><router-link :to="index">首页</router-link></li>
<li><router-link :to="course">课程</router-link></li>
<li><router-link :to="vip">会员</router-link></li>
<li><router-link :to="question">问答</router-link></li>
<script type="text/ecmascript-6">
export default {
data () {
return {
index: '/',
course: '/course',
vip: '/vip',
question: '/question'
}
}
}
</script>
2.默认router-link生成的是a标签,可以更改成其他标签, 使用tag来设置
<li><router-link :to="index" tag="div">首页</router-link></li>
<li><router-link :to="course" tag="div">课程</router-link></li>
<li><router-link :to="vip" tag="div">会员</router-link></li>
<li><router-link :to="question" tag="div">问答</router-link></li>
3.设置router-link的激活样式
第一种方式,在全局配置一个linkActiveClass
配置路由信息(在router文件夹中的index.js中添加)
const router = new VueRouter({
//设置激活样式
linkActiveClass: 'nav-active',
routes: [
{
path: '/',
component: Home
},
......
]
})
第一种方式的优先级小于第二种方式
第二种方式,直接在router-link上增加active-class
默认情况下是支持的点击事件
<li><router-link :to="index" active-class="nav-active">首页</router-link></li>
router-link 的激活样式默认情况下的路由是模糊匹配,例如当前路径是 /article/1 那么也会激活 ,所以当设置 exact-active-class 以后,这个 router-link 只有在当前路由被全包含匹配时才会被激活 exact-active-class 中的 class。
4.设置渲染组件的公共样式,可以直接在router-view上增加class
<router-view class="center"></router-view>
5.可以更改切换的事件,默认是鼠标点击切换,通过设置event实现
<li><router-link :to="index" active-class="nav-active" event="mouseover">首页</router-link></li>
<li><router-link :to="course" event="mouseover">课程</router-link></li>
<li><router-link :to="vip" event="mouseover">会员</router-link></li>
<li><router-link :to="question" event="mouseover">问答</router-link></li>
使用router-link进行的路由跳转执行的都是模糊匹配,如果要实现精准匹配,需要在标签中增加上exact属性。
6.嵌套路由
当路由越来越多的时候,把全部路由都以平级的关系罗列在一起显得那么的没有逻辑,也让路由变得难以维护,为此,我们可以让存在上下级逻辑关系的路由组合成嵌套路由。
比如下面这种场景:点击vip路由之后,还可以通过点击不同的区域去展现vip路由下的“一级会员、二级会员、三级会员”路由页面。
核心代码:需要在vip.vue组件中增加触发路由的 <router-link>,并且在这个页面中放置一个<router-view>,当子路由被匹配到的时候,对应的页面会被渲染到这里。
<template>
<div class="page">
<h1>我是vip会员页面</h1>
<ul class="nav">
<router-link tag="li" to="/vip/one"><a>一级会员</a></router-link>
<router-link tag="li" to="/vip/two"><a>二级会员</a></router-link>
<router-link tag="li" to="/vip/three"><a>三级会员</a></router-link>
</ul>
<!--当路由匹配成功,组件one/two/three会被渲染到这里-->
<router-view></router-view>
</div>
</template>
2.在路由文件夹的index.js文件中,需要引入vip路由嵌套下的三个子路由
import One from '@/components/One'
import Two from '@/components/Two'
import Three from '@/components/Three'
同时为vip路由配置children字段
{
path: '/vip',
component: Vip,
children: [
{
path: 'one', // /vip/one
component: One
},
{
path: 'two',
component: Two
},
{
path: 'three',
component: Three
}
]
}
7.命名路由
有时候,通过一个名称来标识一个路由显得更方便一些,特别是在链接一个路由,或者是执行一些跳转的时候,通俗的说,命名路由就是用name属性给路由取一个名字 例如:
在路由文件夹的index.js文件中,给'/questions'路由增添一个属性name,属性值为这个路由的名字 'wenda'
{
path: '/questions',
name: 'wenda', //注意这里的name值 wenda
component: Questions
},
......
在进行路由的使用这个name属性
<li><router-link exact to="/" >首页</router-link></li>
<li><router-link to="/course" >课程</router-link></li>
<li><router-link to="/vip" >vip</router-link></li>
<!-- 这里使用 name值 -->
<li><router-link :to="{name: 'wenda'}" >问答</router-link></li>
8.命名视图
有时候想同时 (同级) 展示多个视图,而不是嵌套展示,这个时候可以给视图命名,就可以在一个路由中展示多个视图(组件),并实现把指定的路由组件展现到指定的位置。
例如: 在home路由中增加侧边栏和主体内容两个组件
在路由文件夹下的index.js文件中设置路由对应的组件
//新引进的两个命名视图
import Sider from '@/components/Sider'
import HomeContent from '@/components/HomeContent'
......
const router = new VueRouter({
linkActiveClass: 'nav-active',
routes: [
{
path: '/',
components: { //注意这里的components, default设置的组件 被渲染到 <router-view></router-view> 放置的位置
default: Home,
sider: Sider, //Sider组件被渲染到<router-view name=“sider”></router-view> 放置的位置
homecontent: HomeContent //同理
}
},
...
]
})
......
在对应的组件中渲染视图
<template>
<div class="page">
<my-header></my-header>
<router-view></router-view>
<div class="page-main">
<router-view name="sider"></router-view>
<router-view name="homecontent"></router-view>
</div>
</div>
</template>
9.重定向
路由重定向通俗的说就是从一个路由重新定位跳转到另一个路由,例如:访问的 “/a” 重定向到“/b”
重定向也是通过配置routes选项完成的
routes: [
{
path: '/course',
component: Course
},
{
path: '/hello',
component: Hello,
redirect: '/course'
}]
上面代码中,访问 ‘/hello’ 并不会去渲染Hello组件,而是会跳转到路由‘/course’,去渲染course组件
重定向的目标可以是一个命名路由
routes: [
{
path: '/questions',
name: 'wenda',
component: Questions
},
{
path: '/hello',
component: Hello,
redirect: {name: 'wenda'}
}]
上面代码配置后,访问 ‘/hello’就会跳转到命名路由‘wenda’,即‘/questions’,从而去渲染Questions组件
重定向的目标也可以是一个方法,在方法中可以写一些逻辑代码
{
path: '/hello',
component: Hello,
redirect: (to) => {
if (to.hash) {
return '/questions'
} else {
return '/course'
}
}
}
当路径参数对象存在hash值的时候,路由会被跳转到/questions,否则会被跳转到/course
10.别名
别名从字面上理解为别的名字,比如有的人有一个中文名,还有一个别的名字英文名。举例: ‘/hello’ 有一个别名 ‘/hi’, 当用户访问‘/hi’的时候,url会保持不变,但是渲染的还是'/hello'对应的组件,说白了就是不管用户访问的是'/hello',还是‘/hi’,找到的都是同一个人(Hello组件)
routes: [
{
path: '/hello',
component: Hello,
alias: '/hi'
}]
11.编程式导航
首先需要说一下声明式导航,前面我们学过的通过定义导航链接的这种方式叫做声明式导航,特点就是要在template中去写跳转链接,通过生成a标签的形势跳转
<ul>
<li><router-link :to="index" active-class="nav-active" event="mouseover">首页</router-link></li>
<li><router-link :to="course" event="mouseover">课程</router-link></li>
<li><router-link :to="vip" event="mouseover">会员</router-link></li>
<li><router-link :to="question" event="mouseover">问答</router-link></li>
</ul>
编程式导航
编程式导航通俗的说就是vue-router给我们提供了一堆方法,我们通过在js中编写代码来实现导航切换,这些常用方法包括:back、forward、go、push、replace等
push
想要导航到不同的URL,则使用 router.push方法。这个方法会向 history 栈添加一个新的记录
pushHandle () {
// this.$router.push('/hello')
// push内部可以给对象
this.$router.push({name: 'wenda'})
}
注意: 在 Vue 实例内部,你可以通过 $router 访问路由实例
replace
跟 router.push 很像,唯一的不同就是,它不会向 history 添加新记录,而是跟它的方法名一样 —— 替换掉当前的 history 记录
back
back方法可以回到上一步
forward
forward方法可以前进一步
go
go方法可以一次跳多步
注意:go里面如果是正数表示前进,如果是负数表示后退
下面代码代表表示前进2步
goHandle () {
this.$router.go(2)
}
12.动态路由匹配
动态路由意味着不固定,具有某种模式,我们希望通过某种匹配方式,把这种不固定的路由形势映射到同一个组件,例如:一个User组件,不同的ID表示不同的用户,即/user/1、/user/2、/user/3,这些不同用户所对应的路由,我们都希望用一个User组件来渲染。vue-router中提供了动态路径参数来实现这种需求,动态路径参数写法:
routes: [
// 动态路径参数 以冒号开头
{
path: '/user/:id',
component: User
}
]
这里的id类似于一个变量,id可以是1、2、3等具体的值
声明式导航:
<template>
<div class="page">
<router-link :to="'/user/'+item.id" v-for="(item, index) in userList" :key="index">{{item.username}}</router-link>
</div>
</template>
编程式导航:
goToDetail(item) {
this.$router.push({
name: `Detail`,
params: {
id: item.id
}
});
}
路由配置:
?用的是正则表达式,?代表零次或者一次,如果不加问号的话,则id值必须传
routes: [
{
path: '/user/:id?',
component: User
}
]
如何监听/响应动态参数的变化?
方式1: 使用 beforeRouteUpdate 钩子函数
beforeRouteUpdate (to, from, next) {
//to和$route的信息差不多
this.userInfo = this.userList.filter((item) => to.params.id === item.id)[0]
next()
}
方式2: 在组件中对 $route 进行 watch(监听)
watch: {
$route () {
console.log(this.$route)
}
}
路由信息对象$route解析:
- path 对应当前路由的路径
- params 动态路由参数
- query url查询参数
- hash 当前路由的hash值
- fullPath 完整的url路径,包含查询参数和hash
- matched 包含当前路由的所有嵌套路径片段的路由记录
- name 当前路由的名称
13.路由组件传参
路由和对应组件之间的传参:
在组件中使用 $route 会使之与其对应路由形成高度耦合,从而使组件只能在某些特定的 URL 上使用,限制了其灵活性,我们需要做的是通过传参将组件与路由解耦,使得组件的使用更加灵活,这里需要使用到props
路由配置里面我们需要改成这样
只要是对应的路由中设置了props: true,动态路由参数就会自动作为一个属性,传到对应的组件中去,在组件中用porps进行接收之后就可以通过this.属性来使用动态路由中的参数
routes: [
{
path: '/user/:id?',
component: User,
props: true
}
]
在组件的js代码中通过props接收
props: ['id'],
14.记录滚动位置
在路由配置文件中通过以下代码,当导航到新路由时,通过设置滚动行为让页面滚动到你想要的位置。
const router = new VueRouter({
routes: [...],
scrollBehavior (to, from, savedPosition) {
//to-->即将进入的路由
//from--->从哪里来
//savePosition--->保存滚动的位置信息
// 如果检测到记录的位置信息,我们就返回这个位置信息
//如果没有检测到记录的位置信息,我们就返回顶部
if(savePositon){
return savePositon
}else{
return {x:0,y:0}
}
}
}
注意:
这个功能只在支持 history.pushState 的浏览器中可用,和mode没有关系。
高版本的浏览器可以不用写上面那些代码,可以自动实现。
15.vue-router中的全局钩子函数
在vue-router中,路由发生变化,我们可以做一些事情,例如:可以决定是否进入导航,可以决定跳转到哪里,官方文档中又叫做导航守卫。
首先来看一个全局的钩子函数,官方文档中叫做注册一个全局的前置守卫,使用router.beforeEach方法来实现
router.beforeEach(() => {
console.log('beforeEach执行了....')
})
这里的beforeEach可以理解为在每个导航进入之前挂的一个钩子,会在每个导航进入之前出发,在beforeEach里面就可以做一些事情,例如,阻止进入导航,执行上面的代码,会发现我们点击相应的导航,对应的组件并不能渲染出来了,罪魁祸首就是这个beforeEach。
当我们把代码稍作修改:
router.beforeEach((to, from, next) => {
console.log('beforeEach执行了....')
next()
})
组件就可以正常渲染了。
每个路由钩子函数接收三个参数:
to: [Route]: 即将要进入的目标 路由对象
from: [Route]: 当前导航正要离开的路由
next: [Function]: 一定要调用该方法来 resolve 这个钩子
这里的next是一个函数,如果不调用next方法,就不会进入下一个钩子,我们就可以用它来实现跳转或者取消
在写具体跳转或者取消案例之前,插播一个前置知识点:路由元信息
路由元信息就是给每条路由记录配置一个meta字段,字段配置好后可以在需要的地方拿出来使用
配置示例:
{
path: '/',
components: {
default: Home,
a: HomeSider,
b: HomeMain
},
//上面的这个对象称为一个路由记录
//meta我们设置一个全局的,也可以在单个的路由记录中单个设置
meta: {
isLogin: true
}
}
我们可以从钩子函数的参数中拿到meta字段值
router.beforeEach((to, from, next) => {
console.log('beforeEach执行了....')
if (to.meta.isLogin) {
next()
} else {
next(false)
}
})
next函数中传入false就表示不进入导航,我们在meta字段中配置了isLogin,在使用的时候通过to.meta.isLogin 取出来做判断,如果值是true就执行next 如果值是false就执行next(false)
next函数内还可以传入一个路由地址,会自动跳到改地址,我可以把上面代码稍作修改
router.beforeEach((to, from, next) => {
console.log('beforeEach执行了....')
if (to.meta.isLogin) {
next()
} else {
next('/login')
}
})
除了在导航进入之前有一个钩子函数,在导航进入之后也有一个钩子函数,叫做afterEach,使用方法和beforeEach类似, 因为afterEach执行时已经进入到导航了,所以没有第三个参数next
router.afterEach((to, from) => {
console.log('afterEach执行了....')
// 判断是否有title字段
if (to.meta.title) {
window.document.title = to.meta.title
} else {
window.document.title = 'hello'
}
})
16.vue-router中写到路由记录里的钩子函数
beforeEnter 在进入导航前被调用,需要在路由记录里面配置
{
path: '/course',
component: Course,
meta: {
isLogin: false,
title: '好好学编程'
},
beforeEnter (to, from, next) {
console.log('beforeEnter被执行了')
}
},
17.写在组件内部的路由钩子函数
1.beforeRouteEnter, 在导航进入前被调用,在这个函数里面不能拿到this,因为即将被渲染的组件还没被创建
beforeRouteEnter (to, from, next) {
console.log('user组件中的beforeRouteEnter执行了')
next()
}
2.beforeRouteUpdate,在当前路由改变,但是该组件被复用时调用,举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。 可以访问组件实例 this
beforeRouteUpdate (to, from, next) {
console.log('user组件中的beforeRouteUpdate执行了')
next()
}
3.beforeRouteLeave,导航离开该组件的对应路由时调用,可以访问组件实例 this
beforeRouteLeave (to, from, next) {
console.log('user组件中的beforeRouteLeave执行了')
next()
}
18.路由切换的动画效果
vue中封装了一套transtion组件,可以提供过渡效果
<transition>
<router-view></router-view>
</transition>
通过过渡css类名的方式来设置过渡效果
过渡的类名称有:
- v-enter:定义进入过渡的开始状态。在元素被插入之前生效,在元素被插入之后的下一帧移除。
- v-enter-active:定义进入过渡生效时的状态。在整个进入过渡的阶段中应用,在元素被插入之前生效,在过渡/动画完成之后移除。这个类可以被用来定义进入过渡的过程时间,延迟和曲线函数。
- v-enter-to: 2.1.8版及以上 定义进入过渡的结束状态。在元素被插入之后下一帧生效 (与此同时 v-enter 被移除),在过渡/动画完成之后移除。
- v-leave: 定义离开过渡的开始状态。在离开过渡被触发时立刻生效,下一帧被移除。
- v-leave-active:定义离开过渡生效时的状态。在整个离开过渡的阶段中应用,在离开过渡被触发时立刻生效,在过渡/动画完成之后移除。这个类可以被用来定义离开过渡的过程时间,延迟和曲线函数。
- v-leave-to: 2.1.8版及以上 定义离开过渡的结束状态。在离开过渡被触发之后下一帧生效 (与此同时 v-leave 被删除),在过渡/动画完成之后移除。
其中,v- 这个前缀是可以自定义的,例如:
.fade-enter {
transform: translateX(-100%);
opacity: 0;
}
.fade-enter-to {
transform: translateX(0);
opacity: 1;
}
.fade-enter-active {
transition: 1s;
}
.fade-leave {
transform: translateX(0);
opacity: 1;
}
.fade-leave-to {
transform: translateX(100%);
opacity: 0;
}
.fade-leave-active{
transition: 1s;
}
我们把类名称的前缀自定义成了“fade-”, 这里需要注意的是前缀可以自定义,但是后面的enter、enter-to不能自定义,自定义类名称后,需要在transition组件上加name属性
<transition mode="out-in" name="fade">
<router-view></router-view>
</transition>
上面代码中,我们发现有个mode属性,这个属性表示设置过渡的模式, out-in,通俗的说就是先离开,再进入,同时还有另一种模式,in-out,先进入,再离开