Vue全家桶系列之Vue-router(五)
文章目录
- 1.前言
- 2.路由过渡效果
- 3.编程式的导航
- 4.导航守卫
- 4.1 全局守卫
- 4.2 路由独享的守卫
- 4.3组件内的守卫
- 4.4导航解析流程
1.前言
上篇文章说了vue-router的路由命名视图,动态路由匹配,路由滚动行为,接下来再看看其他的用法
2.路由过渡效果
过渡效果类似css中的过滤,我们用的是transititon的封装组件来为路由添加过渡动画的,首先来看个图了解下有哪些过渡的css类名,Enter就是路由的进入,是从Opacity 0到1(淡入),Leave就是路由离开是从Opacity 1到0(淡出),v-enter-active,v-leave-active是活动的状态,可以被用来定义过渡的时间,延迟和曲线函数等
v-enter: 定义进入过渡的开始状态
v-enter-active: 定义进入活动状态
v-enter-to: 定义进入的结束状态
v-leave: 定义离开过渡的开始状态
v-leave-active: 定义离开活动状态
v-leave-to: 定义离开的结束状态
下面我们来简单的模拟一个淡入淡出,过渡为1秒的效果,看下面代码
<template><div class="router-class"><div><router-link to="/" exact>home页面</router-link><router-link to="/about" active-class="about-router">about页面</router-link><router-link to="/help" active-class="help-router">help页面</router-link><router-link to="/user" active-class="help-router">user页面</router-link></div><transition mode="out-in"><router-view class="default"></router-view></transition></div>
</template><style>
.v-enter {opacity: 0;
}
.v-enter-to {opacity: 1;
}
.v-enter-active {transition: 1s;
}.v-leave {opacity: 1;
}
.v-leave-to {opacity: 0;
}
.v-leave-active {transition: 1s;
}
</style>
上面用了mode为out-in的过渡模式!
out-in: 当前元素先进行过渡,完成之后新元素过渡进入
in-out: 新元素先进行过渡,完成之后当前元素过渡离开
如果不想用v开头的class,我们想自定义样式就需要用到name这个属性,我们可以定义left,right,然后动态的设置name属性,我们可以设置路由的元信息 (index),可以根据目标路由和正要离开的路由的index比较来判断到底用的是left还是right过滤效果,我们先来为路由设置元信息
export default new Router({linkActiveClass: "active",mode: 'history',routes: [{path: '/',component: Home,meta:{index:0},},{path: '/home',name: 'home',components: {default: Home,sidebar: Sidebar}},{path: '/about',component: About,children: [{path: '',name: 'work',component: Work,meta:{index:1},},{path: 'company',name: 'company',component: Company,},{path: 'contactUs',name: 'contactUs',component: ContactUs,}]},{path: '/help',name: 'help',alias: "/123",component: Help,meta:{index:2},},{path: '/user/:id?',name: 'user',component: User,meta:{index:3},}]
})
然后设置transition的name属性,注意这里是动态设置,所以需要加上冒号
<transition name="className"><router-view class="default"></router-view>
</transition>
最后监听$route
export default {name: "App",data() {return {className: "",};},watch: {$route(to, from) {console.log(to.meta.index);console.log(from.meta.index);//目标的路由index大于离开路由的index就用left效果,否则用right效果this.className = to.meta.index > from.meta.index ? "left" : "right";},},
};
看下整个代码
<template><div class="router-class"><div><router-link to="/" exact>home页面</router-link><router-link to="/about" active-class="about-router">about页面</router-link><router-link to="/help" active-class="help-router">help页面</router-link><router-link to="/user" active-class="help-router">user页面</router-link></div><transition :name="className"><router-view class="default"></router-view></transition></div>
</template><script>
export default {name: "App",data() {return {className: "",};},watch: {$route(to, from) {console.log(to.meta.index);console.log(from.meta.index);this.className = to.meta.index > from.meta.index ? "left" : "right";},},
};
</script><style>
.active {background-color: yellow;
}
.about-router {background-color: pink;
}
.help-router {background-color: skyblue;
}
.sidebar {width: 250px;height: 1000px;float: left;border: 1px solid #aaa;
}
.default {height: 2000px;border: 1px solid #aaa;
}.left-enter {transform: translateX(-100%);
}.left-enter-to {transform: translateX(0);
}
.left-enter-active {transition: 1s;
}.left-leave {transform: translateX(0);
}.left-leave-to {transform: translateX(100%);
}
.left-leave-active {transition: 1s;
}.right-enter {transform: translateX(100%);
}.right-enter-to {transform: translateX(0);
}
.right-enter-active {transition: 1s;
}.right-leave {transform: translateX(0);
}.right-leave-to {transform: translateX(-100%);
}
.right-leave-active {transition: 1s;
}
</style>
看效果,点击右边路由是right效果,点击左边路由是left效果
3.编程式的导航
之前我们都是用<router-link>来定义导航链接,我们也可以利用借助于 router 的实例方法,通过编写代码来实现导航的切换,看下router 的实例有哪些方法:
push 导航到不同url,向 history 栈添加一个新的记录
replace 导航到不同url,替换 history 栈中当前记录
back 回退一步
forward 前进一步
go 指定前进回退步数
添加3个button按钮
<button @click="click">跳转到help页面</button>
<button @click="back">后退一步</button>
<button @click="forward">前进一步</button>
借助路由的方法来实现跳转,前进,后退功能
methods: {click() {// this.$router.push({path:'/help'})this.$router.push("/help");},back() {//等价于 this.$router.go(-1); 负数为后退几步this.$router.back();},forward() {//等价于 this.$router.go(1);正数数为前进几步this.$router.forward();},
}
push 和replace 都可以导航都不同的url上 ,唯一的不同就是,replace 它不会向 history 添加新记录,而是跟它的方法名一样替换掉当前的 history 记录,看下面效果!
4.导航守卫
所谓导航守卫就是导航钩子函数,导航钩子函数有什么用,当导航发生变化时,导航钩子主要用来拦截导航,让它完成跳转或取消,有点类似vue的生命周期的钩子函数,在不同的时期可以做不同的操作,下面来看下有哪几种导航钩子函数!
4.1 全局守卫
全局就会想到全局变量,就是每个函数都可以用的到变量,那么全局守卫也就是每个路由导航都可以触发的钩子函数呗,下面来设置一个全局前置守卫
router.beforeEach((to,from,next)=>{console.log(1);next();
})
那么每次当一个导航触发时,都会打印1,看下面结果
to是将要进入的目标路由对象,from是当前导航正要离开的路由,next是个函数,只有调用这个next()才会进入下个导航,next(false)或者不写就会中断当前的导航,当然你可以自定义 路径next('/help'),写法和< router-link to='/help'>类似,也可以传入 next 的参数是一个 Error 实例,看下面代码
router.beforeEach((to,from,next)=>{console.log(1);next(new Error('导航错误'));
})
router.onError((error)=>{console.log(error);
})
点击每个导航都不起作用,注意看右边打印的错误
全局前置守卫用的比较多的就是验证信息,好比一个登录验证,那些页面需要验证,哪些不需要验证,来做个最简单的模拟,我们在路由元信息里面加个isLogin来判断首次进入是否需要登录,比如about,user需要验证,其他页面不需要验证
let router = new Router({linkActiveClass: "active",mode: 'history',routes: [{path: '/',component: Home,},{path: '/home',name: 'home',components: {default: Home,sidebar: Sidebar}},{path: '/about',component: About,children: [{path: '',name: 'work',component: Work,meta: {isLogin: true},},{path: 'company',name: 'company',component: Company,},{path: 'contactUs',name: 'contactUs',component: ContactUs,}]},{path: '/help',name: 'help',alias: "/123",component: Help,},{path: '/user/:id?',name: 'user',component: User,meta: {isLogin: true},},{path: '/login',name: 'login',component: Login}]
});router.beforeEach((to, from, next) => {if (to.meta.isLogin) {next('/login');} else {next();}
})
运行下代码,当点击到about,user会跳到登录页面,看下面效果
有全局前置守卫是不是应该也有全局后置守卫,和前置守卫不同的是少了一个next参数,所以它不会改变导航本身,那么这个全局后置守卫又有什么用呢?举个例子,当路由跳转了,导航改变了,是不是页面的tilt也应该相应的改变把,下面我们也来试试下,还是在元信息里面去设置title
let router = new Router({linkActiveClass: "active",mode: 'history',routes: [{path: '/',component: Home,meta: {title: "Home"}},{path: '/home',name: 'home',components: {default: Home,sidebar: Sidebar}},{path: '/about',component: About,children: [{path: '',name: 'work',component: Work,meta: {title: "About"}},{path: 'company',name: 'company',component: Company,},{path: 'contactUs',name: 'contactUs',component: ContactUs,}]},{path: '/help',name: 'help',alias: "/123",component: Help,meta: {title: "Help"}},{path: '/user/:id?',name: 'user',component: User,meta: {title: "User"}}]
});router.afterEach((to, from) => {if (to.meta.title) {window.document.title = to.meta.title;}
})
看下面执行效果,仔细看页面顶部的tile的变化
有了全局前置后置守卫,其实还有一个全局解析守卫( 2.5.0+),它在夹在全局前置和全局后置之间,和前置守卫类似(参数一样),区别是在导航被确认之前,同时在所有组件内守卫和异步路由组件被解析之后,解析守卫就被调用。下面会说到什么是组件内的守卫下面会说,意思是这个全局解析守卫会在异步路由被解析,路由被解析了那么路由里面还有组件,路由里面组件守卫也需要被调用之后,这个全局解析守卫才会被调用,现在说可能有点模糊,后面会说到整个执行过程!
4.2 路由独享的守卫
上面是全局的钩子函数,针对所有的路由,那么这个是单个路由的钩子函数,就只针对某个路由有效,很好理解!我们现在单独对use路由进行登录验证,当跳到use路由需要登录验证
{path: '/user/:id?',name: 'user',component: User,beforeEnter:(to, from,next)=>{if(to.path=="/user"){router.push('/login');}}
}
因为这个钩子函数只针对单个路由的钩子函数,所以你切换其他的路由是不会触发这个钩子函数的,看下面效果
4.3组件内的守卫
最后说到这个组件内的守卫,vue也有类似的钩子函数 beforeCreate,created,beforeMount,mounted ,beforeUpdate ,updated,beforeDestroy,destroyed。路由组件也有3个钩子函数,beforeRouteEnter,beforeRouteUpdate,beforeRouteLeave我们来一个个的看下有什么作用,先说beforeRouteEnter,看下面代码
beforeRouteEnter(to, from, next) {console.log("beforeRouteEnter");next();},beforeCreate() {console.log("beforecreated");},created() {console.log("created");},beforeMount() {console.log("beforeMount");},mounted() {console.log("mounted ");},
看下执行顺序,beforeRouteEnter居然比beforeCreate还早执行, 在执行beforeCreate时,实例组件刚开始创建,元素dom和数据都还没有初始化
所以在路由beforeRouteEnter钩子函数中组件this实例是undefined,但是我们也可以通过next回调函数来获取组件实例
beforeRouteEnter(to, from, next) {next((vm)=>{console.log(vm.$route);});
}
打印出组件实例的路由对象如下
在当前路由改变,如果是同一个组件,那么组件里面生命周期函数是不会再次调用的,那么这时候就用到beforeRouteUpdate这个钩子函数,之前做user组件时候用过,其实user/123,user/345,user/456用的是一个组件(只是展示数据不一样,功能还是一样的),每组信息都有个唯一的id,我们可以通过id不同来渲染不用的用户信息,因为三个路由都渲染同个组件,所以组件的生命周期钩子created函数不会再被调用,这时候我们可以用watch来监听路由的变化或者调用beforeRouteUpdate钩子函数,还是来看下之前的代码
let data = [{id: "123",name: "张三",sex: "男",age: 18,sprot: "running",skill: "javascript",},{id: "345",name: "李四",sex: "男",age: 28,sprot: "footabll",skill: "java",},{id: "456",name: "小丽",sex: "女",age: 20,sprot: "swimming",skill: "node.js",},
];
export default {name: "User",data() {return {userList: data,user: {},};},beforeRouteUpdate(to, from, next) {//获取当前路由的动态id,然后找到对应的数据来展示this.getUser(to.params.id);next();},created() {this.getUser(this.$route.params.id);},methods: {getUser(id) {let item = this.userList.find((us) => {return us.id == id;});this.user = item;},},
这个钩子函数可以访问到组件实例,下图为当点击不用的用户展示出不用的数据效果
最后来说下beforeRouteLeave这个钩子函数,字面意思也很明显,路由离开的时候调用的钩子函数,我们可以通过这个函数来判断当前路由是否可以离开(比如数据没保存,或者数据没填写完整),比如当切换到user导航,只有点击了保存按钮才能离开,要不然不让离开,我们来模拟下!
<button @click="clickSave">保存</button>export default {name: "User",data() {return {userList: data,user: {},save:false};},beforeRouteUpdate(to, from, next) {//获取当前路由的动态id,然后找到对应的数据来展示this.getUser(to.params.id);next();},beforeRouteLeave (to, from, next){if(this.save){next();}else{next(false)}}, created() {this.getUser(this.$route.params.id);},methods: {getUser(id) {let item = this.userList.find((us) => {return us.id == id;});this.user = item;},clickSave(){this.save=true;}}
};
看下面执行效果
4.4导航解析流程
现在我们来模拟下两个场景,一个导航切换到另外一个导航(use页面切换到help页面),我们把测试的导航守卫钩子函数都加上,首先在index.js加上全局的钩子函数(beforeEach,beforeResolve,afterEach),use和help路由也分别加上路由独享的守卫钩子函数(beforeEnter )
import Vue from 'vue'
import Router from 'vue-router'
import About from '@/components/About'
import Help from '@/components/Help'
import Home from '@/components/Home'
import Work from '@/components/Work'
import Company from '@/components/Company'
import ContactUs from '@/components/ContactUs'
import Sidebar from '@/components/Sidebar'
import User from '@/components/User'
import Login from '@/components/Login'
Vue.use(Router)
let router = new Router({linkActiveClass: "active",mode: 'history',routes: [{path: '/',component: Home,},{path: '/about',component: About,children: [{path: '',name: 'work',component: Work,},{path: 'company',name: 'company',component: Company,},{path: 'contactUs',name: 'contactUs',component: ContactUs,}]},{path: '/help',name: 'help',alias: "/123",component: Help,beforeEnter: (to, from, next) => {console.log("beforeEnter");next();},},{path: '/user/:id?',name: 'user',component: User,beforeEnter: (to, from, next) => {console.log("beforeEnter");next();},}]
});router.beforeEach((to, from, next)=>{console.log("beforeEach");next();
})router.beforeResolve((to, from, next)=>{console.log("beforeResolve");next();
})router.afterEach((to, from) => {console.log("afterEach")
})
export default router
然后再help组件和use组件里面分别加上组件内的守卫钩子函数(beforeRouteEnter,beforeRouteUpdate ,beforeRouteLeave)
export default {name: "Help",beforeRouteEnter(to, from, next) {console.log("beforeRouteEnter");next(() => {console.log("next回调");});},beforeRouteUpdate(to, from, next) {console.log("beforeRouteUpdate");next();},beforeRouteLeave(to, from, next) {console.log("beforeRouteLeave");next();},
};
当从use导航切换到help导航,注意右边的钩子函数的顺序,先是user组件的beforeRouteLeave==>全局的beforeEach==>help路由的beforeEnter==>help组件的beforeRouteEnter==>全局的beforeResolve==>全局的afterEach==>help组件beforeRouteEnter的next回调
当然如果你是从直接刷新help导航,而不是从use切换过来的,那么就不会有user组件的beforeRouteLeave,然后help导航切换到use导航也是一样的
当切换use里面的组件(动态路由匹配的,共用一个组件),这里组件里面只有beforeRouteUpdate会被调用,其他的钩子函数不会被调用
所以对于一个带有动态参数的路径之间跳转的时候,由于会渲染同样的组件,它的钩子执行顺序为:全局的beforeEach==>use组件的beforeRouteEnter==>全局的beforeResolve==>全局的afterEach,所以我们可以得出个总结(网上搬运的,省的自己画了)
所以完整的导航解析流程是:
导航被触发。
在失活的组件里调用 beforeRouteLeave 守卫。
调用全局的 beforeEach 守卫。
在重用的组件里调用 beforeRouteUpdate 守卫 (2.2+)。
在路由配置里调用 beforeEnter。
解析异步路由组件。
在被激活的组件里调用 beforeRouteEnter。
调用全局的 beforeResolve 守卫 (2.5+)。
导航被确认。
调用全局的 afterEach 钩子。
触发 DOM 更新。
调用 beforeRouteEnter 守卫中传给 next 的回调函数,创建好的组件实例会作为回调函数的参数传入
Vue全家桶系列之Vue-router(五)相关推荐
- Vue全家桶系列之Vuex(二)
文章目录 1.前言 2.Vuex如何使用 3.State 4.Mutation 5.Getter 6.Vue.js devtools 7.Action 8.Module 1.前言 上篇文章介绍了vue ...
- Vue全家桶系列之Vuex(三)
文章目录 1.前言 2.模块的命名空间 3.模块的动态注册 4.vuex严格模式 5.vuex插件 5.1 subscribe 5.2 subscribeAction 5.3 replaceState ...
- Vue全家桶系列之Vuex(一)
文章目录 1.前言 2.Vuex是什么? 3.什么是集中式状态管理模式? 4.什么情况下使用Vuex?它能帮我们解决什么问题? 5.简单的例子 1.前言 Vuex是一个很棒的状态管理库,它很简单并且非 ...
- Vue全家桶学习笔记:Vue Router篇
前言 学完了一堆后端的知识之后呢,又去学了下git- 嘛,现在又回到了前端的学习 前置内容:
- vue全家桶系列之网易云音乐(移动版)
一个精致的网易云音乐webapp 网易云音乐(移动版) api来源(感谢Binaryify不断更新的网易云音乐接口,这也将是这个项目不断拓展下去的坚实依托) 源码地址 项目预览(web端在chrome ...
- 使用vue全家桶搭建的vue小说阅读器,已部署到服务器可预览
暑假实习了几个月辞职后,闲着无聊自己开发的一个vue小说阅读器链接 请使用浏览器打开此链接 http://39.96.55.152(由于域名需要备案用的是ip地址),里面的小说接口调用的是追书神器, ...
- 使用vue全家桶搭建的vue小说阅读器,已部署到服务器可预览。
暑假实习了几个月辞职后,闲着无聊自己开发的一个vue小说阅读器链接 http://39.96.55.152(由于域名需要备案用的是ip地址),里面的小说接口调用的是追书神器,然后我把里面的vip和收费 ...
- Vue全家桶 + webpack 构建单页应用初体验
文章指南 主题 承接这上一篇Vue + Webpack 构建模块化开发框架详解,我们知道了如何使用webpack对vue进行打包,从而开始我们的前端模块化开发之路,这一篇在上一篇的基础上讲解 Vu ...
- VUE全家桶 REACT jQuery
VUE VUE.js是一款JavaScript框架,Vue.js(读音 /vjuː/, 类似于 view) 是一套构建用户界面的渐进式框架. VUE只关注视图层,Vue 的目标是通过尽可能简单的 AP ...
最新文章
- shell中的PS命令的含义
- java中==和equals引发的思考
- oracle数据库非归档模式数据备份和恢复
- SSRF服务器端请求伪造
- 在eclipse中利用条件断点打印log
- 东大OJ-一元三次方程解的个数
- HashMap 在 JDK 1.8 中新增的数据结构 – 红黑树
- poj 1504 Adding Reversed Numbers
- 设计师配色宝典:教你从零开始学配色
- 360浏览器显示服务器拒绝连接,360浏览器提示“您与此网站之间建立的连接不安全完美解决方法...
- python PIL生成gif帧率问题
- 企鹅号不更新会封_我们如何为企鹅基金会筹集60,000美元
- HBase--JavaAPI的操作,创建表修改表,增删改查数据
- vue-echarts数据统计图表展示
- MP4和HR-HDTV压制教程
- 费舍尔精确检验在关联分析中的应用
- ROS报错Error:cannot launch node of type [map_server/map_server]
- 解决 iOS 上 transform rotate 兼容问题
- SAP DOI实现小记
- Java导出Kml或Kmz格式文件
热门文章
- Java练习题-09
- BatchNormalization、LayerNormalization、InstanceNorm、GroupNorm、SwitchableNorm总结
- 动物 v.s. AI奥运会:你会赌一只鸟还是机器人夺冠?
- .xml配置文件中The reference to entity serverTimezone must end with the ';' delimiter.错误
- 百度AI评测:新闻摘要
- 洛谷 P3258 [JLOI2014]松鼠的新家 树上差分
- Mixly第32课~第34课,课程学习笔记 | Mixly米思齐纯干货系列
- 网页设计与制作第三节文字(页面设计与制作答案)
- 都说IT行业饱和了,2023年成为程序员还有发展前景吗?
- Android解析JSON,你真的需要三方库?