[Toc] >[success] # 导航守卫 ~~~ 1.vuerouter 一套钩子来触发路由在不同阶段触发的函数 2.导航守卫分成三大块'全局守卫','路由独享守卫','组件内守卫' 3.'全局守卫' 顾名思义所有路由在进入跳转的时候都会触发,整个'全局路 由'分为三个阶段依次是'beforeEach'、'beforeResolve'、'afterEach' 4.'路由独享守卫' 顾名思义只在定义路由和路由组件中的对象中使用它有一 个阶段'beforeEnter' 5.'组件内守卫'顾名思义只在组件中触发的路由内容它有三个阶段依次 是'beforeRouteEnter'、'beforeRouteUpdate'、'beforeRouteLeave' ~~~ >[info] ## 导航守卫中三个参数讲解 ~~~ 1.to和from 都是route的对象,因此可以使用route中获取路由参数的所有方 法,官方api'https://router.vuejs.org/zh/api/#%E8%B7%AF%E7%94%B1%E5%AF%B9%E8%B1%A1' 2.next 是跳转下一个守卫用的,也可以指定守卫,可用的参数和router-link 中的to一致的 3.官方的具体讲解: to: Route: 即将要进入的目标 路由对象 from: Route: 当前导航正要离开的路由 next: Function: 一定要调用该方法来 resolve 这个钩子。执行效果依赖 next 方法的调用参数。 next(): 进行管道中的下一个钩子。如果全部钩子执行完了,则导航的状态就是 confirmed (确认的)。 next(false): 中断当前的导航。如果浏览器的 URL 改变了 (可能是用户手动或者浏览器后退按钮),那么 URL 地址会重置到 from 路由对应的地址。 next('/') 或者 next({ path: '/' }): 跳转到一个不同的地址。当前的导航被中断,然后进行一个新的导航。你可以向 next 传递任意位置对象,且允许设置诸如 replace: true、name: 'home' 之类的选项以及任何用在 router-link 的 to prop 或 router.push 中的选项。 next(error): (2.4.0+) 如果传入 next 的参数是一个 Error 实例,则导航会被终止且该错误会被传递给 router.onError() 注册过的回调。 ~~~ >[info] ## 全局守卫 ~~~ 1.全局守卫因为可以监听到所有路由,在触发的时候可以用来做判断用户是 否登录,这种全局配置 2.'beforeEach' -- 官方给的中文名字叫'全局前置守卫'在路由刚开始触发且还 未进入,路由对应的组件中,简单的理解最早触发,但是触发时候没有任何 组件一类的加载,正因为如此适合做登陆判断逻辑。 3.'beforeResolve'在导航被确认之前,同时在路由独享守卫和所有组件内守卫和异步路由组件被解析之后,解析守卫就被调用 4.'afterEach' 在所有路由的生命周期结束后调用,简单的说就是整个路由周 期都完事了,这个阶段可以做一场景,当我们在加载后台数据的时候经常会做一个蒙 版这个蒙版上会有一个加载的转圈的团,'afterEach'是整个路由结束阶段,因此也可以 理解成是整个组件加载完阶段,所以在这个时候控制蒙版消失最好 ~~~ >[danger] ##### 案例 ~~~ 1.下面代码执行 完后打印的数据顺序: beforeEach 生命周期 beforeResolve 生命周期 afterEach 生命周期 2.要注意是'afterEach ' 整个生命周期都已经结束了,所以不需要在使用 next()往下继续传递执行了。 ~~~ ~~~ <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> <title>Examples</title> <meta name="description" content=""> <meta name="keywords" content=""> <link href="" rel="stylesheet"> <script src="https://cdn.jsdelivr.net/npm/vue@2.5.22/dist/vue.js"></script> <script src="https://unpkg.com/vue-router/dist/vue-router.js"></script> </head> <body> <div id="app"> <router-link to='/app/123'>sapp</router-link> <router-link to='/app/456'>sapp</router-link> <router-view></router-view> </div> <template id="sapp"> <div> {{$route.params.id}} </div> </template> <script type="text/javascript"> const sapp = { template:'#sapp', }; const routes=[ { path:"/app/:id", component:sapp, }, ]; const router = new VueRouter({ routes // (缩写) 相当于 routes: routes }); // 验证用户登录 router.beforeEach((to, from, next)=>{ console.log('beforeEach 生命周期'); //next('/login') 接受的参数和router-link一致 next(); }); router.beforeResolve((to, from, next)=>{ console.log('beforeResolve 生命周期'); next() }); // 跳转之后执行 没有next router.afterEach((to, from)=>{ console.log('afterEach 生命周期'); }); var vms = new Vue({ el: "#app", router, }) </script> </body> </html> ~~~ >[info] ## 路由独享守卫 ~~~ 1.路由独享守卫 中只有'路由独享守卫' -- 'beforeEnter' 阶段,是当前组件路 由进入的时候触发的阶段 ~~~ >[danger] ##### 案例 ~~~ 1.下面案例触发顺序展示的是全局守卫和路由守卫整体的生命周期: beforeEach 生命周期 beforeEnter 路由中的路由 beforeResolve 生命周期 afterEach 生命周期 ~~~ ~~~ <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> <title>Examples</title> <meta name="description" content=""> <meta name="keywords" content=""> <link href="" rel="stylesheet"> <script src="https://cdn.jsdelivr.net/npm/vue@2.5.22/dist/vue.js"></script> <script src="https://unpkg.com/vue-router/dist/vue-router.js"></script> </head> <body> <div id="app"> <router-link to='/app/123'>sapp</router-link> <router-link to='/app/456'>sapp</router-link> <router-view></router-view> </div> <template id="sapp"> <div> {{$route.params.id}} </div> </template> <script type="text/javascript"> const sapp = { template:'#sapp', }; const routes=[ { path:"/app/:id", component:sapp, beforeEnter(to, from, next){ console.log('beforeEnter 路由中的路由'); next(); } }, ]; const router = new VueRouter({ routes // (缩写) 相当于 routes: routes }); // 验证用户登录 router.beforeEach((to, from, next)=>{ console.log('beforeEach 生命周期'); //next('/login') 接受的参数和router-link一致 next(); }); router.beforeResolve((to, from, next)=>{ console.log('beforeResolve 生命周期'); next() }); // 跳转之后执行 没有next router.afterEach((to, from)=>{ console.log('afterEach 生命周期'); }); var vms = new Vue({ el: "#app", router, }) </script> </body> </html> ~~~ >[info] ## 组件内的守卫 ~~~ 1.组件内守卫分三个阶段: 'beforeRouteEnter'、'beforeRouteUpdate'、'beforeRouteLeave'三个阶段会在不同时刻触发 2.'beforeRouteEnter' 在还没有进入该组件之前触发,一般用获取异步请求 ,可以替代vue生命周期中获取数据请求 3.'beforeRouteUpdate'在动态路由中使用,由于动态路由中切换路由的时候 ,犹豫绑定的是同一个组件因此在不会在重新渲染,但是为了可以让组件中 的内容重新渲染,有两种方法第一种使用watch 监听,但这种需要使 用'props'写法,另一种就是在'beforeRouteUpdate' 它会监听到动态路由的改 变,因此可以在这个周期中异步动态路由对应的数据 4.'beforeRouteLeave' 离开组件的时候触发,可以对表单一些校验未提交的 时候触发询问用户是否到下一个页面官方的说法这个离开守卫通常用来禁止 用户在还未保存修改前突然离开。该导航可以通过 next(false) 来取消。 ~~~ >[danger] ##### 官方对组件守卫的特殊说明 ~~~ const Foo = { template: `...`, beforeRouteEnter (to, from, next) { // 在渲染该组件的对应路由被 confirm 前调用 // 不!能!获取组件实例 `this` // 因为当守卫执行前,组件实例还没被创建 }, beforeRouteUpdate (to, from, next) { // 在当前路由改变,但是该组件被复用时调用 // 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候, // 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。 // 可以访问组件实例 `this` }, beforeRouteLeave (to, from, next) { // 导航离开该组件的对应路由时调用 // 可以访问组件实例 `this` } } ~~~ * 想利用 'beforeRouteEnter ' 特性给组件获取异步值 ~~~ 1.可以通过传一个回调给 next来访问组件实例。在导航被确认的时候执行回 调,并且把组件实例作为回调方法的参数。 2.注意 beforeRouteEnter 是支持给 next 传递回调的唯一守卫。对于 beforeRouteUpdate 和 beforeRouteLeave 来说,this 已经可用了,所以不 支持传递回调,因为没有必要了。 beforeRouteEnter (to, from, next) { next(vm => { // 通过 `vm` 访问组件实例 }) } ~~~ >[danger] ##### 案例 ~~~ 1.下面案例中当点击左侧sapp连接的生命周期如下: 'beforeEach 生命周期' 'beforeEnter 路由中的路由' 'beforeRouteEnter 组件中的路由' 'beforeResolve 生命周期' 'afterEach 生命周期' 2.点击完左侧在点击右侧sapp的生命周期,不会在触发路由的守卫: 'beforeEach 生命周期' 'beforeRouteUpdate 组件中的路由' 'beforeResolve 生命周期' 'afterEach 生命周期' 3.点击test先触发组件离开的守卫在依次触发全局守卫: 'beforeRouteLeave 组件中的路由' 'beforeEach 生命周期' 'test -- beforeEnter 路由中的路由' 'test -- beforeRouteEnter 组件中的路由' 'beforeResolve 生命周期' 'afterEach 生命周期' ~~~ ~~~ <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> <title>Examples</title> <meta name="description" content=""> <meta name="keywords" content=""> <link href="" rel="stylesheet"> <script src="https://cdn.jsdelivr.net/npm/vue@2.5.22/dist/vue.js"></script> <script src="https://unpkg.com/vue-router/dist/vue-router.js"></script> </head> <body> <div id="app"> <router-link to='/app/123'>sapp</router-link> <router-link to='/app/456'>sapp</router-link> <router-link to='/test'>test</router-link> <router-view></router-view> </div> <template id="sapp"> <div> {{$route.params.id}} </div> </template> <script type="text/javascript"> const sapp = { // 获取输出,在路由触发前获取数据,但是此时获取不到路由的this对象 beforeRouteEnter(to, from, next){ console.log('beforeRouteEnter 组件中的路由'); //next(); next(vm=>{ console.log(vm) }) }, // 同一个组件不同路由触发 beforeRouteUpdate(to, from, next){ console.log('beforeRouteUpdate 组件中的路由') next() }, // 表单修改 beforeRouteLeave (to, from, next){ console.log('beforeRouteLeave 组件中的路由') next() }, props:['name'], template:'#sapp', }; const test = { beforeRouteEnter(to, from, next){ console.log('test -- beforeRouteEnter 组件中的路由'); //next(); next(vm=>{ console.log(vm) }) }, beforeRouteUpdate(to, from, next){ console.log('test -- beforeRouteUpdate 组件中的路由') next() }, beforeRouteLeave (to, from, next){ console.log('test -- beforeRouteLeave 组件中的路由') next() }, template:'<h1>123</h1>'} const routes=[ { path:"/app/:id", component:sapp, beforeEnter(to, from, next){ console.log('beforeEnter 路由中的路由'); next(); } }, { path:'/test', component:test, beforeEnter(to, from, next){ console.log('test -- beforeEnter 路由中的路由'); next(); } }, ]; const router = new VueRouter({ routes // (缩写) 相当于 routes: routes }); // 验证用户登录 router.beforeEach((to, from, next)=>{ console.log('beforeEach 生命周期'); //next('/login') 接受的参数和router-link一致 next(); }); router.beforeResolve((to, from, next)=>{ console.log('beforeResolve 生命周期'); next() }); // 跳转之后执行 没有next router.afterEach((to, from)=>{ console.log('afterEach 生命周期'); }); var vms = new Vue({ el: "#app", router, }) </script> </body> </html> ~~~ >[info] ## 官方的守卫导航周期总结 ~~~ 1.导航被触发。 2.在失活的组件里调用离开守卫。 3.调用全局的 beforeEach 守卫。 4.在重用的组件里调用 beforeRouteUpdate 守卫 (2.2+)。 5.在路由配置里调用 beforeEnter。 6.解析异步路由组件。 7.在被激活的组件里调用 beforeRouteEnter。 8.调用全局的 beforeResolve 守卫 (2.5+)。 9.导航被确认。 10.调用全局的 afterEach 钩子。 11.触发 DOM 更新。 12.用创建好的实例调用 beforeRouteEnter 守卫中传给 next 的回调函数。 ~~~