导航守卫

vue-router 提供的导航守卫主要用来通过跳转或取消的方式守卫导航。有多种机会植入路由导航过程中:全局的, 单个路由独享的, 或者组件级的。其实,导航守卫就是路由跳转过程中的一些钩子函数,再直白点路由跳转是一个大的过程,这个大的过程分为跳转前中后等等细小的过程,在每一个过程中都有一函数,这个函数能让你操作一些其他的事儿的时机,这就是导航守卫。

研究导航守卫,先做一个demo看看各种守卫的触发时机:

//main.js
//全局导航守卫
router.beforeEach((to, from, next) => {console.log("全局导航守卫:beforeEach()");next(()=>{console.log("全局导航守卫:beforeEach()--->next()");});
}),
router.beforeResolve((to, from, next) => {console.log("全局导航守卫:beforeResolve()");next(()=>{console.log("全局导航守卫:beforeResolve()--->next()");});
}),
router.afterEach((to, from) => { //没有next()console.log("全局导航守卫:afterEach()");
}),//router/index.js
{path: '/guard/:id',name: 'Guard',component: () => import('../views/Guard.vue'),//独享导航守卫beforeEnter: (to, from, next) => {console.log("独享导航守卫:beforeEnter()");next(() => {console.log("独享导航守卫:beforeEnter()--->next()");});}
},//Guard.vue
<template><div>路由守卫测试页面{{$route.params.id}}</div>
</template><script>export default {//组件导航守卫,不调用next()将导致导航终止beforeRouteEnter(to, from, next) {console.log("组件导航守卫:beforeRouteEnter()");next(() => {console.log("组件导航守卫:beforeRouteEnter()--->next()");});},beforeRouteUpdate(to, from, next) {console.log("组件导航守卫:beforeRouteUpdate()");next(() => {console.log("组件导航守卫:beforeRouteUpdate()--->next()");});},beforeRouteLeave(to, from, next) {console.log("组件导航守卫:beforeRouteLeave()");next(() => {console.log("组件导航守卫:beforeRouteLeave()--->next()");});},//组件生命周期beforeCreate() {console.log("组件生命周期:beforeCreate()");},created() {console.log("组件生命周期:created()");},beforeMount() {console.log("组件生命周期:beforeMount()");},mounted() {console.log("组件生命周期:mounted()");}}
</script>

运行:

全局守卫

全局守卫是指路由实例上直接操作的钩子函数,他的特点是所有路由配置的组件都会触发,直白点就是触发路由就会触发这些钩子函数,包括beforeEach、beforeResolve(2.5+)、afterEach

  • beforeEach:在路由跳转前触发,参数包括to,from,next(参数会单独介绍)三个,这个钩子作用主要是用于登录验证,也就是路由还没跳转提前告知,以免跳转了再通知就为时已晚。
  • beforeResolve:这个钩子和beforeEach类似,也是路由跳转前触发,参数也是to,from,next三个,和beforeEach区别官方解释为:是在导航被确认之前,同时在所有组件内守卫和异步路由组件被解析之后,解析守卫就被调用。即在 beforeEach 和 组件内beforeRouteEnter 之后,afterEach之前调用。
  • afterEach:他是在路由跳转完成后触发,参数包括to,from,但不接受next,他发生在beforeEach和beforeResolve之后,beforeRouteEnter之前。
//main.js
router.beforeEach((to, from, next) => {//...next();
}),
router.beforeResolve((to, from, next) => {//...next();
}),
router.afterEach((to, from) => { //...//没有next()
}),

独享守卫

是指在单个路由配置的时候也可以设置,只有一个钩子函数beforeEnter。
beforeEnter:和beforeEach完全相同,如果都设置则在beforeEach之后紧随执行,参数to、from、next。

const router = new VueRouter({routes: [{path: '/foo',component: Foo,beforeEnter: (to, from, next) => {// ...}}]
})

组件守卫

是指在组件内执行的钩子函数,类似于组件内的生命周期,相当于为配置路由的组件添加的生命周期钩子函数。包括beforeRouteEnter、beforeRouteUpdate (2.2+)、beforeRouteLeave三个。

  • beforeRouteEnter:路由进入之前调用,参数包括to,from,next。该钩子在全局守卫beforeEach和独享守卫beforeEnter之后,全局beforeResolve和全局afterEach之前调用,要注意的是该守卫内访问不到组件的实例,也就是this为undefined,也就是他在beforeCreate生命周期前触发。在这个钩子函数中,可以通过传一个回调给 next来访问组件实例。在导航被确认的时候执行回调,并且把组件实例作为回调方法的参数。
  • beforeRouteUpdate:在当前路由改变时,并且该组件被复用时调用,可以通过this访问实例。参数包括to,from,next。对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,组件实例会被复用,该守卫会被调用。当前路由query变更时,该守卫也会被调用。
  • beforeRouteLeave:导航离开该组件的对应路由时调用,可以访问组件实例this,参数包括to,from,next。通常用来禁止用户在还未保存修改前突然离开。该导航可以通过 next(false) 来取消。
const Foo = {template: `...`,// 在渲染该组件的对应路由被 confirm 前调用beforeRouteEnter (to, from, next) {// 不能获取组件实例`this`,因为当守卫执行前,组件实例还没被创建next(vm => {// 通过 `vm` 访问组件实例})},//在当前路由改变,但是该组件被复用时调用beforeRouteUpdate (to, from, next) {// 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,// 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。// 可以访问组件实例 `this`},// 导航离开该组件的对应路由时调用beforeRouteLeave (to, from, next) {// 可以访问组件实例 `this`const answer = window.confirm('确定要离开?当前页面还未保存!')if (answer) {next()} else {next(false)}}
}

next

上面的守卫导航除afterEach都用到了next函数参数。一定要调用该方法来 resolve 守卫,否则守卫 resolve 完之前一直处于 等待中。执行效果依赖 next 方法的调用参数。
next(): 进行管道中的下一个钩子。如果全部钩子执行完了,则导航的状态就是 confirmed (确认的)。

  • next(false): 中断当前的导航。如果浏览器的 URL 改变了 (可能是用户手动或者浏览器后退按钮),那么 URL 地址会重置到 from 路由对应的地址。
  • next(’/’) 或者 next({ path: ‘/’ }): 跳转到一个不同的地址。当前的导航被中断,然后进行一个新的导航。你可以向 next 传递任意位置对象,且允许设置诸如 replace: true、name: ‘home’ 之类的选项以及任何用在 router-link标签 的to属性或 router.push() 中的选项。
  • next(error): (2.4.0+) 如果传入 next 的参数是一个 Error 实例,则导航会被终止且该错误会被传递给 router.onError() 注册过的回调。

确保 next() 在任何给定的导航守卫中都被严格调用一次。它可以出现多于一次,但是只能在所有的逻辑路径都不重叠的情况下,否则钩子永远都不会被解析或报错。示例:

// 错误
router.beforeEach((to, from, next) => {// 如果用户未能验证身份,则 `next` 会被调用两次if (to.name !== 'Login' && !isAuthenticated) {next({ name: 'Login' });}next();
})// 正确
router.beforeEach((to, from, next) => {if (to.name !== 'Login' && !isAuthenticated) {next({ name: 'Login' });}else{next();}
})

数据获取

有时候,进入某个路由后,需要从服务器获取数据。例如,在渲染用户信息时需要从服务器获取用户的数据。

导航完成后获取数据

在组件的 created()中获取数据,就能在数据获取期间展示一个 loading 状态,还可以在不同视图间展示不同的 loading 状态。

<template><div class="post"><div v-if="loading" class="loading">Loading...</div><div v-if="error" class="error">{{ error }}</div><div v-if="post" class="content"><h2>{{ post.title }}</h2><p>{{ post.body }}</p></div></div>
</template><script>export default {data () {return {loading: false,post: null,error: null}},created () {// 组件创建完后从服务器获取数据,此时 data 已经被 observed 了this.fetchData()},watch: {// 如果路由有变化,会再次执行该方法'$route': 'fetchData'},methods: {fetchData () {this.error = this.post = nullthis.loading = true// replace getPost with your data fetching util / API wrappergetPost(this.$route.params.id, (err, post) => {this.loading = falseif (err) {this.error = err.toString()} else {this.post = post}})}}
}
</script>

在导航完成前获取数据

通过这种方式,我们在导航转入新的路由前获取数据。即 beforeRouteEnter 守卫中获取数据,当数据获取成功后调用 next 方法。

export default {data () {return {post: null,error: null}},beforeRouteEnter (to, from, next) {getPost(to.params.id, (err, post) => {next(vm => vm.setData(err, post))})},// 路由改变前,组件就已经渲染完了,set数据逻辑稍稍不同beforeRouteUpdate (to, from, next) {this.post = nullgetPost(to.params.id, (err, post) => {this.setData(err, post)next()})},methods: {setData (err, post) {if (err) {this.error = err.toString()} else {this.post = post}}}
}

路由元信息

定义路由的时候可以配置 meta 字段。然后通过$route.meta可以获取到。

// router/index.js{path: '/meta',name: 'Meta',component: () => import('../views/Meta.vue'),meta: { //父类添加了meta作为需要登陆的标志requiresAuth: true },children: [{path: 'child',name: 'MetaChild',component: () => import('../views/MetaChild.vue'),}]}// main.js// 全局守卫router.beforeEach((to, from, next) => {//to.matched是一个数组,包含匹配到相关的 路由记录,路由记录可以是嵌套的,以上如果访问/meta/child会为['meta的路由信息','meta/child的路由信息']if (to.matched.some(record => record.meta.requiresAuth)) {console.log(to.name+"页面要登录的,重定向到home登录");next({path: '/',})} else {console.log(to.name+"页面不用登录");next();}
}),

运行:

滚动行为

当且仅当 popstate 导航 (通过浏览器的 前进/后退 按钮触发) 时想要页面滚到某个位置可以使用此特性。

// router/index.js
let posY = 0; //保存滚动位置
const router = new VueRouter({routes: [...],scrollBehavior(to, from, savedPosition) {//savedPosition传入的是每次跳出页面时的偏移,要保存起来给下次浏览器前进/回退跳回来用if (from.name == 'Scroll' && (savedPosition)) {console.log("Scroll从页面跳出,位置被保存")posY = savedPosition.y;} else if (to.name == 'Scroll') {console.log("跳转到Scroll,位置被读取")return {x: 0,y: posY};} else {console.log("没有位置信息")return {x: 0,y: 0};}}
})//vue
<template><div><ul><li v-for="(item,index) in list" :key="index">{{index}}</li></ul></div>
</template><script>export default {data() {return {list: new Array(1000)}}}
</script>

运行,从Scroll.vue跳转首页后回退,即可看到之前保存的偏移:

过渡动画

vue Transition 的所有动画功能都可以用在路由过渡上,形如:

<transition><router-view></router-view>
</transition>

一个简单案例:

//css
/* 可以设置不同的进入和离开动画 */
/* 设置持续时间和动画函数 */
.slide-fade-enter-active {transition: all .3s ease;
}
.slide-fade-leave-active {transition: all .8s cubic-bezier(1.0, 0.5, 0.8, 1.0);
}
.slide-fade-enter, .slide-fade-leave-to
/* .slide-fade-leave-active for below version 2.1.8 */ {transform: translateX(10px);opacity: 0;
}//vue
<template><div id="app">//name设置使用哪个动效。mode设置播放模式,不设置播放模式可能导致“动效错乱”<transition name="slide-fade" mode="out-in"><router-view /></transition></div>
</template>

运行:

只对某个组件过渡只需要加在该组件上:

// About.vue
<template><transition name="fade" mode="out-in"><div class="about"><h1>This is an about page</h1></div></transition>
</template>

动态选择动效:

<!-- 使用动态的 transition name -->
<transition :name="transitionName"><router-view></router-view>
</transition>// 接着在父组件内
// watch $route 决定使用哪种过渡
watch: {'$route' (to, from) {const toDepth = to.path.split('/').lengthconst fromDepth = from.path.split('/').lengththis.transitionName = toDepth < fromDepth ? 'slide-right' : 'slide-left'}
}

路由懒加载

当打包构建应用时,JavaScript 包会变得非常大,影响页面加载。如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就更加高效了。

异步加载

// route/index.js// 非异步组件
import Home from '../views/Home.vue'
const router = new VueRouter({routes: [{ path: '/home', component: Home }]
})// 能够被 Webpack 自动代码分割的异步组件
const Foo = () => import('./Foo.vue')
const router = new VueRouter({routes: [{ path: '/foo', component: Foo }]
})

代码分块

想把某个路由下的所有组件都打包在同个异步块 (chunk) 中。只需要使用 命名 chunk,一个特殊的注释语法来提供 chunk name (需要 Webpack > 2.4)。

// route/index.js//以下三个组件都会被打包到 group-foo
const Foo = () => import(/* webpackChunkName: "group-foo" */ './Foo.vue')
const Bar = () => import(/* webpackChunkName: "group-foo" */ './Bar.vue')
const Baz = () => import(/* webpackChunkName: "group-foo" */ './Baz.vue')

相关资料

Vue Router

24.Vue路由管理器:Router(进阶篇)相关推荐

  1. 前端学习(1274):Vue路由管理器

  2. Vue中路由管理器Vue Router使用介绍(三)

    2019独角兽企业重金招聘Python工程师标准>>> 一.路由定义添加动态参数定义 1.路由定义项,使用:xx 方式 定义动态参数 {path:'/user/:id/:name', ...

  3. vue-router路由管理器的使用

    vue-router路由管理器的使用 一.vue-router路由入门 1.什么是router(路由)? 这里的路由并不是指我们平时所说的硬件路由器,在vue里是指单页应用的路径管理器,再通俗点来说, ...

  4. Vue Router路由管理器

    目录: 相关理解 基本路由 几个注意事项 嵌套(多级)路由 路由的query参数 命名路由 路由的params参数 路由的props配置 路由跳转的replace方法 编程式路由导航 缓存路由组件 a ...

  5. Vue原理解析:手写路由管理器 —— Vue-Router

    由于时间问题,暂时先把代码完整的贴上来,感兴趣的朋友可以自行研究或收藏.等我那时有时间的时候,对专栏文章进行排序,并逐一讲解代码 一.对外暴露的入口文件index.js import Vue from ...

  6. vue从入门到精通之进阶篇(一)vue-router基础

    路由原理 传统开发方式 url改变后 立刻发起请求,响应整个页面,渲染整个页面 SPA 锚点值改变后 不会发起请求,发起ajax请求,局部改变页面数据 页面不跳转 用户体验更好 SPA single ...

  7. Vue状态管理器state(Vuex)

    一.什么是Vuex Vuex是一个专为Vue.js应用程序开发的状态管理模式 采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化 二.什么是状态管理模式 状态自管 ...

  8. python上下文是什么_(1条消息)python使用@contextmanager来定义上下文管理器(一篇文章,彻底明白!码文并茂,简单明了)...

    什么是上下文管理器? 上下文管理器是在Python2.5之后加入的功能,可以在方便的需要的时候比较精确地分配和释放资源, with便是上下文管理器的最广泛的应用 with open("tes ...

  9. python使用@contextmanager来定义上下文管理器(一篇文章,彻底明白!码文并茂,简单明了)和 yield 和 __enter__ 和 __exit__

    什么是上下文管理器? 上下文管理器是在Python2.5之后加入的功能,可以在方便的需要的时候比较精确地分配和释放资源, with便是上下文管理器的最广泛的应用 with open("tes ...

最新文章

  1. 中文NER任务简析与深度算法模型总结和实战展示 转 作者原创的不错,很有水平,需要研读
  2. 【RocketMQ】【分布式事务】使用RocketMQ实现分布式事务
  3. 支付宝app支付总结
  4. Rainbow的相关资料
  5. CYQ.Data 从入门到放弃ORM系列:开篇:自动化框架编程思维
  6. 剑指offer-孩子们的游戏(圆圈中最后剩下的数)
  7. Matlab:Matlab中常用的函数、案例详细攻略
  8. 【LeetCode 502】IPO
  9. 如何成为数据科学家_成为数据科学家需要了解什么
  10. 融云通讯服务器,vue使用融云即时通讯,老是报了发送失败,服务器超时
  11. 通过lvm方式扩展swap分区
  12. 人工智能搜索算法案例分析
  13. 对抗雾霾的健康饮食注意
  14. 2022年Google I/O 大会即将举行,可领取 2022 年 I/O 大会参会开发者资料徽章。
  15. Java中文英文数字混合掩码_Java8 中文教程
  16. Rdt2.1 和 Rdt2.2的详细解释
  17. 一个优秀的国产ITSM软件应该有哪些特性?
  18. 酒店预订分销系统和分销商
  19. WiFi认证过程需要的协议和服务
  20. Firebase常用功能和官方Demo简介

热门文章

  1. 2022时事政治考试题库精选
  2. Mesos和Marathon简介和浅析
  3. 九九届财经学校计算机专业,对外经济贸易大学信息学院
  4. 2019-9-2-正则表达式30分钟入门教程
  5. javascript 对象{‘key1’:‘value1‘,‘key2‘:‘value2‘} 转化为数组 [{‘key1‘:‘value1‘},{‘key2‘:‘name2‘}]
  6. 【奥卡姆剃刀】一条短信骗走所有财产的分析
  7. 怎么在网页上嵌入新浪微博页面
  8. ST-GCN源码运行demo配置过程(openpose-1.5.0+win10)
  9. 办公型计算机配置清单,办公电脑配置清单_推荐_办公电脑配置全攻略!
  10. 什么是code-Behind技术?