文章目录

  • 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是活动的状态,可以被用来定义过渡的时间,延迟和曲线函数等

  1. v-enter: 定义进入过渡的开始状态
  2. v-enter-active: 定义进入活动状态
  3. v-enter-to: 定义进入的结束状态
  4. v-leave: 定义离开过渡的开始状态
  5. v-leave-active: 定义离开活动状态
  6. 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的过渡模式!

  1. out-in: 当前元素先进行过渡,完成之后新元素过渡进入
  2. 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 的实例有哪些方法:

  1. push 导航到不同url,向 history 栈添加一个新的记录
  2. replace 导航到不同url,替换 history 栈中当前记录
  3. back 回退一步
  4. forward 前进一步
  5. 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,所以我们可以得出个总结(网上搬运的,省的自己画了)

所以完整的导航解析流程是:

  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 的回调函数,创建好的组件实例会作为回调函数的参数传入

Vue全家桶系列之Vue-router(五)相关推荐

  1. Vue全家桶系列之Vuex(二)

    文章目录 1.前言 2.Vuex如何使用 3.State 4.Mutation 5.Getter 6.Vue.js devtools 7.Action 8.Module 1.前言 上篇文章介绍了vue ...

  2. Vue全家桶系列之Vuex(三)

    文章目录 1.前言 2.模块的命名空间 3.模块的动态注册 4.vuex严格模式 5.vuex插件 5.1 subscribe 5.2 subscribeAction 5.3 replaceState ...

  3. Vue全家桶系列之Vuex(一)

    文章目录 1.前言 2.Vuex是什么? 3.什么是集中式状态管理模式? 4.什么情况下使用Vuex?它能帮我们解决什么问题? 5.简单的例子 1.前言 Vuex是一个很棒的状态管理库,它很简单并且非 ...

  4. Vue全家桶学习笔记:Vue Router篇

    前言 学完了一堆后端的知识之后呢,又去学了下git- 嘛,现在又回到了前端的学习 前置内容:

  5. vue全家桶系列之网易云音乐(移动版)

    一个精致的网易云音乐webapp 网易云音乐(移动版) api来源(感谢Binaryify不断更新的网易云音乐接口,这也将是这个项目不断拓展下去的坚实依托) 源码地址 项目预览(web端在chrome ...

  6. 使用vue全家桶搭建的vue小说阅读器,已部署到服务器可预览

    暑假实习了几个月辞职后,闲着无聊自己开发的一个vue小说阅读器链接 请使用浏览器打开此链接  http://39.96.55.152(由于域名需要备案用的是ip地址),里面的小说接口调用的是追书神器, ...

  7. 使用vue全家桶搭建的vue小说阅读器,已部署到服务器可预览。

    暑假实习了几个月辞职后,闲着无聊自己开发的一个vue小说阅读器链接 http://39.96.55.152(由于域名需要备案用的是ip地址),里面的小说接口调用的是追书神器,然后我把里面的vip和收费 ...

  8. Vue全家桶 + webpack 构建单页应用初体验

    文章指南 主题   承接这上一篇Vue + Webpack 构建模块化开发框架详解,我们知道了如何使用webpack对vue进行打包,从而开始我们的前端模块化开发之路,这一篇在上一篇的基础上讲解 Vu ...

  9. VUE全家桶 REACT jQuery

    VUE VUE.js是一款JavaScript框架,Vue.js(读音 /vjuː/, 类似于 view) 是一套构建用户界面的渐进式框架. VUE只关注视图层,Vue 的目标是通过尽可能简单的 AP ...

最新文章

  1. shell中的PS命令的含义
  2. java中==和equals引发的思考
  3. oracle数据库非归档模式数据备份和恢复
  4. SSRF服务器端请求伪造
  5. 在eclipse中利用条件断点打印log
  6. 东大OJ-一元三次方程解的个数
  7. HashMap 在 JDK 1.8 中新增的数据结构 – 红黑树
  8. poj 1504 Adding Reversed Numbers
  9. 设计师配色宝典:教你从零开始学配色
  10. 360浏览器显示服务器拒绝连接,360浏览器提示“您与此网站之间建立的连接不安全完美解决方法...
  11. python PIL生成gif帧率问题
  12. 企鹅号不更新会封_我们如何为企鹅基金会筹集60,000美元
  13. HBase--JavaAPI的操作,创建表修改表,增删改查数据
  14. vue-echarts数据统计图表展示
  15. MP4和HR-HDTV压制教程
  16. 费舍尔精确检验在关联分析中的应用
  17. ROS报错Error:cannot launch node of type [map_server/map_server]
  18. 解决 iOS 上 transform rotate 兼容问题
  19. SAP DOI实现小记
  20. Java导出Kml或Kmz格式文件

热门文章

  1. Java练习题-09
  2. BatchNormalization、LayerNormalization、InstanceNorm、GroupNorm、SwitchableNorm总结
  3. 动物 v.s. AI奥运会:你会赌一只鸟还是机器人夺冠?
  4. .xml配置文件中The reference to entity serverTimezone must end with the ';' delimiter.错误
  5. 百度AI评测:新闻摘要
  6. 洛谷 P3258 [JLOI2014]松鼠的新家 树上差分
  7. Mixly第32课~第34课,课程学习笔记 | Mixly米思齐纯干货系列
  8. 网页设计与制作第三节文字(页面设计与制作答案)
  9. 都说IT行业饱和了,2023年成为程序员还有发展前景吗?
  10. Android解析JSON,你真的需要三方库?