失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > vue-router进阶:路由使用归纳 路由导航守卫 导航守卫解析流程

vue-router进阶:路由使用归纳 路由导航守卫 导航守卫解析流程

时间:2021-06-14 08:19:41

相关推荐

vue-router进阶:路由使用归纳 路由导航守卫 导航守卫解析流程

一、定义和理解

导航守卫的作用vue-router提供的导航守卫主要用来通过跳转或取消的方式守卫导航。

》》项目中应用场景举例:路由页面跳转时候进行登陆验证;路由跳转判断;

有多种机会植入路由导航过程中全局路由, 单个路由独享的, 或者组件级的

全局守卫包括:router.beforeEach(是全局前置守卫)、router.beforeResolve(是全局解析守卫)、router.afterEach(是全局后置钩子)

单个路由独享的导航守卫:在路由配置上直接定义beforeEnter守卫

组件内守卫包括:在组件内定义路由的导航守卫,有beforeRouteEnter 、beforeRouteUpdate 、beforeRouteLeave

每个导航守卫都接受3个参数 from 、to、next(),参数的定义如下:

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() 注册过的回调。

注意事项:

1、确保要调用next方法,否则钩子就不会被 resolved。

2、当一个导航触发时,导航守卫按照顺序调用。守卫是异步解析执行,导航在所有守卫 resolve 完之前一直处于等待中

二、导航守卫使用

首先vue-router知识回顾:

0、在vue项目中使用vue-router步骤概述:

// 0. 如果使用模块化机制编程,导入Vue和VueRouter,要调用 Vue.use(VueRouter)// 1. 定义 (路由) 组件。// 可以从其他文件 import 进来const Foo = { template: '<div>foo</div>' }const Bar = { template: '<div>bar</div>' }// 2. 定义路由// 每个路由应该映射一个组件。 其中"component" 可以是// 通过 Vue.extend() 创建的组件构造器,// 或者,只是一个组件配置对象。// 我们暂时不套路讨论嵌套路由、传参、元数据、导航守卫等。const routes = [{ path: '/foo', component: Foo },{ path: '/bar', component: Bar }]// 3. 创建 router 实例,然后传 `routes` 配置// 你还可以传别的配置参数, 不过先这么简单着吧。const router = new VueRouter({routes // (缩写) 相当于 routes: routes})// 4. 创建和挂载根实例。// 记得要通过 router 配置参数注入路由,// 从而让整个应用都有路由功能const app = new Vue({router}).$mount('#app')// 现在,应用已经启动了!

1、可以在任何组件内通过this.$router访问路由器,也可以通过this.$route访问当前路由:

// Home.vueexport default {computed: {username () {// 我们很快就会看到 `params` 是什么return this.$route.params.username}},methods: {goBack () {window.history.length > 1? this.$router.go(-1): this.$router.push('/')}}}

2、动态路由匹配时使用this.$route.params获取路由的路径参数

应用场景:需要把某种模式匹配到的所有路由,全都映射到同个组件。例如,我们有一个User组件,对于所有 ID 各不相同的用户,都要使用这个组件来渲染。又像/user/foo/user/bar都将映射到相同的路由。

注意:可以在一个路由中设置多段“路径参数”,如下:

当使用路由参数时,例如从/user/foo导航到/user/bar原来的组件实例会被复用。因为两个路由都渲染同个组件,比起销毁再创建,复用则显得更加高效。不过,这也意味着组件的生命周期钩子不会再被调用

参数或查询的改变并不会触发进入/离开的导航守卫。可以通过观察$route对象来应对这些变化,或使用beforeRouteUpdate的组件内守卫。

3、复用组件时,想对路由参数的变化作出响应的话,你可以简单地 watch (监测变化)$route对象:

const User = {template: '...',watch: {'$route' (to, from) {// 对路由变化作出响应...}}}

或者使用 2.2 中引入的beforeRouteUpdate导航守卫:

const User = {template: '...',beforeRouteUpdate (to, from, next) {// react to route changes...// don't forget to call next()}}

4、在router配置文件中使用通配符 (*)捕获所有路由或 404 Not found 路由

注意:通常含有通配符的路由应该放在最后。路由{ path: '*' }通常用于客户端 404 错误。如果你使用了History 模式,请确保正确配置你的服务器。

同一个路径可以匹配多个路由,此时,匹配的优先级就按照路由的定义顺序:谁先定义的,谁的优先级就最高

当使用一个通配符时,$route.params内会自动添加一个名为pathMatch参数。它包含了 URL 通过通配符被匹配的部分:

// 给出一个路由 { path: '/user-*' }this.$router.push('/user-admin')this.$route.params.pathMatch // 'admin'// 给出一个路由 { path: '*' }this.$router.push('/non-existing')this.$route.params.pathMatch // '/non-existing'

5、使用vue-router嵌套路由时,在路由定义文件中使用children配置:

注意事项:以/开头的嵌套路径会被当作根路径。 这让你充分的使用嵌套组件而无须设置嵌套的路径。

const router = new VueRouter({routes: [{ path: '/user/:id', component: User,children: [{// 当 /user/:id/profile 匹配成功,// UserProfile 会被渲染在 User 的 <router-view> 中path: 'profile',component: UserProfile},{// 当 /user/:id/posts 匹配成功// UserPosts 会被渲染在 User 的 <router-view> 中path: 'posts',component: UserPosts}]}]})

6、vue-router路由导航可以在组件中使用<router-link :to="...">的形式外,还有JS编程式的导航(包括可以调用this.$router.push(location, onComplete?, onAbort?)router.replace(location, onComplete?, onAbort?),以及router.go(n))

注意事项:

如果提供了pathparams会被忽略;如果目的地和当前路由相同,只有参数发生了改变 (比如从一个用户资料到另一个/users/1->/users/2),你需要使用beforeRouteUpdate来响应这个变化 (比如抓取用户信息);router.push方法。这个方法会向 history 栈添加一个新的记录,所以,当用户点击浏览器后退按钮时,则回到之前的 URL。router.replace方法它不会向 history 添加新记录,而是跟它的方法名一样 —— 替换掉当前的 history 记录。在 2.2.0+,可选的在router.pushrouter.replace中提供onCompleteonAbort回调作为第二个和第三个参数。这些回调将会在导航成功完成 (在所有的异步钩子被解析之后) 或终止 (导航到相同的路由、或在当前导航完成之前导航到另一个不同的路由) 的时候进行相应的调用。

const userId = '123'router.push({ name: 'user', params: { userId }}) // -> /user/123router.push({ path: `/user/${userId}` }) // -> /user/123// 这里的 params 不生效router.push({ path: '/user', params: { userId }}) // -> /user// 在浏览器记录中前进一步,等同于 history.forward()router.go(1)// 后退一步记录,等同于 history.back()router.go(-1)

router.pushrouter.replacerouter.gowindow.history.pushStatewindow.history.replaceStatewindow.history.go好像, 实际上它们确实是效仿window.historyAPI 的。

7、当需要在一个组件页面展示多个路由视图 router-view ,需要给视图命名:

<router-view class="view one"></router-view><router-view class="view two" name="a"></router-view><router-view class="view three" name="b"></router-view>

const router = new VueRouter({routes: [{path: '/',components: {default: Foo,a: Bar,b: Baz}}]})

注意:正确使用components配置 (带上 s):

8、vue-router路由重定向和别名使用

“重定向”的意思是,当用户访问/a时,URL 将会被替换成/b,然后匹配路由为/b;重定向不能指向自身,会形成死循环。

“别名”的意思是给路由取“小名”,/a的别名是/b,意味着,当用户访问/b时,URL 会保持为/b,但是路由匹配则为/a,就像用户访问/a一样。

const router = new VueRouter({routes: [{ path: '/a', redirect: '/b' },{ path: '/b', redirect: { name: 'foo' }},{ path: '/c', redirect: to => {// 方法接收 目标路由 作为参数// return 重定向的 字符串路径/路径对象}},{ path: '/a', component: A, alias: '/b' }]})

9、vue-router使用路由进行组件传参时,通过props取代 $route.params 可以降低耦合度:

注意:在组件中使用$route会使之与其对应路由形成高度耦合,从而使组件只能在某些特定的 URL 上使用,限制了其灵活性。通过props取代,这样你便可以在任何地方使用该组件,使得该组件更易于重用和测试。

如果props被设置为trueroute.params将会被设置为组件属性。

const User = { //$route 的耦合template: '<div>User {{ $route.params.id }}</div>'}const router = new VueRouter({routes: [{ path: '/user/:id', component: User }]})const User = { //通过 props 解耦props: ['id'],template: '<div>User {{ id }}</div>'}const router = new VueRouter({routes: [{ path: '/user/:id', component: User, props: true },// 对于包含命名视图的路由,你必须分别为每个命名视图添加 `props` 选项:{path: '/user/:id',components: { default: User, sidebar: Sidebar },props: { default: true, sidebar: false }}]})

如果props是一个对象,它会被按原样设置为组件属性。当props是静态的时候有用。

const router = new VueRouter({routes: [{ path: '/promotion/from-newsletter', component: Promotion, props: { newsletterPopup: false } }]})

你可以创建一个函数返回props。这样你便可以将参数转换成另一种类型,将静态值与基于路由的值结合等等。

const router = new VueRouter({routes: [{ path: '/search', component: SearchUser, props: (route) => ({ query: route.query.q }) }]})

开始导航守卫使用demo:

注意事项:注意导航守卫并没有应用在跳转路由上,而仅仅应用在其目标上。

1、全局前置守卫router.beforeEach

const router = new VueRouter({ ... })router.beforeEach((to, from, next) => {// ...})

2、全局解析守卫router.beforeResolve

在导航被确认之前,同时在所有组件内守卫和异步路由组件被解析之后,解析守卫就被调用。

3、全局后置钩子router.afterEach

和守卫不同的是,这些钩子不会接受next函数也不会改变导航本身:

router.afterEach((to, from) => {// ...})

4、路由独享的守卫router.beforeEnter

是在路由配置上直接定义beforeEnter守卫:

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

5、组件内的守卫beforeRouteEnter、beforeRouteUpdate(2.2 新增)、beforeRouteLeave

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守卫不能访问this的解决方式是:通过传一个回调给next来访问组件实例。在导航被确认的时候执行回调,并且把组件实例作为回调方法的参数。

注意:beforeRouteEnter是支持给next传递回调的唯一守卫。对于beforeRouteUpdatebeforeRouteLeave来说,this已经可用了,所以不支持传递回调,因为没有必要了。

beforeRouteEnter (to, from, next) {next(vm => {// 通过 `vm` 访问组件实例})}

这个离开守卫通常用来禁止用户在还未保存修改前突然离开。该导航可以通过next(false)来取消。

beforeRouteLeave (to, from , next) {const answer = window.confirm('Do you really want to leave? you have unsaved changes!')if (answer) {next()} else {next(false)}}

三、导航守卫解析流程:

导航被触发。在失活的组件里调用离开守卫。调用全局的beforeEach守卫。在重用的组件里调用beforeRouteUpdate守卫 (2.2+)。在路由配置里调用beforeEnter。解析异步路由组件。在被激活的组件里调用beforeRouteEnter。调用全局的beforeResolve守卫 (2.5+)。导航被确认。调用全局的afterEach钩子。触发 DOM 更新。用创建好的实例调用beforeRouteEnter守卫中传给next的回调函数。

四、其他:路由元信息、过渡动效、数据获取、滚动位置、懒加载

路由元信息:

定义路由的时候可以设置meta字段,可以存储该路由相关信息(例如:设置每个路由的title,取路由的title设置为选项卡的标题);还可以用来设置页面权限要求

{path: '/router2',name: 'router2',component:router2,meta:{title:"router2"}}

// 全局前置守卫router.beforeEach((to,from,next) => {console.log(to);console.log(from);if(to.meta.title) {document.title = to.meta.title;} else {document.title = '我是默认的title'}next();});

路由过渡动效:

Vue 在插入、更新或者移除 DOM 时,提供多种不同方式的应用过渡效果。

const Foo = {template: `<transition name="slide"><div class="foo">...</div></transition>`}const Bar = {template: `<transition name="fade"><div class="bar">...</div></transition>`}<!-- 使用动态的 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'}}

路由数据获取:(分别有导航完成前和完成后获取)

有时候,进入某个路由后,需要从服务器获取数据。例如,在渲染用户信息时,你需要从服务器获取用户的数据。我们可以通过两种方式来实现:

导航完成之后获取:先完成导航,然后在接下来的组件生命周期钩子中获取数据。在数据获取期间显示“加载中”之类的指示。导航完成之前获取:导航完成前,在路由进入的守卫中获取数据,在数据获取成功后执行导航。从技术角度讲,两种方式都不错 —— 就看你想要的用户体验是哪种。

导航完成后获取数据,我们会马上导航和渲染组件,然后在组件的created钩子中获取数据。

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}})}}}

我们在导航转入新的路由前获取数据。我们可以在接下来的组件的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))})},// 路由改变前,组件就已经渲染完了// 逻辑稍稍不同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}}}}

路由滚动行为:

使用前端路由,当切换到新路由时,想要页面滚到顶部,或者是保持原先的滚动位置,就像重新加载页面那样。vue-router能做到,而且更好,它让你可以自定义路由切换时页面如何滚动。

注意: 这个功能只在支持history.pushState的浏览器中可用。

当创建一个 Router 实例,你可以提供一个scrollBehavior方法:

const router = new VueRouter({routes: [...],scrollBehavior (to, from, savedPosition) {// return 期望滚动到哪个的位置}})

scrollBehavior方法接收tofrom路由对象。第三个参数savedPosition当且仅当popstate导航 (通过浏览器的 前进/后退 按钮触发) 时才可用。

这个方法返回滚动位置的对象信息,长这样:

{ x: number, y: number }{ selector: string, offset? : { x: number, y: number }}(offset 只在 2.6.0+ 支持)

如果返回一个 falsy (译者注:falsy 不是false,参考这里)的值,或者是一个空对象,那么不会发生滚动。

举例:

scrollBehavior (to, from, savedPosition) {return { x: 0, y: 0 }}

对于所有路由导航,简单地让页面滚动到顶部。

返回savedPosition,在按下 后退/前进 按钮时,就会像浏览器的原生表现那样:

scrollBehavior (to, from, savedPosition) {if (savedPosition) {return savedPosition} else {return { x: 0, y: 0 }}}

如果你要模拟“滚动到锚点”的行为:

scrollBehavior (to, from, savedPosition) {if (to.hash) {return {selector: to.hash}}}

路由懒加载:

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

结合 Vue 的异步组件和 Webpack 的代码分割功能,轻松实现路由组件的懒加载。

一个能够被 Webpack 自动代码分割的异步组件。

const Foo = () => import('./Foo.vue')

在路由配置中什么都不需要改变,只需要像往常一样使用Foo

const router = new VueRouter({routes: [{ path: '/foo', component: Foo }]})

五、应用demo

vue路由登陆验证:

前言:vue的项目的登录状态如果用vuex状态管理,页面一刷新vuex管理的状态就会消失,所以,会将登录的状态写到web Storage中进行存储管理。

步骤:

1、router/index.js 》在需要登录验证的路由元信息里加入登录验证标识requiresAuth:true

routers: [{ path: '/home',name: 'home',component: Home,meta: { requiresAuth: true }},{path: '/login',name: 'Login',component: Login},]

2、登陆组件从服务端获取到token,将登录状态写入web Storage里

login() {this.$api.login().then(function(res) {//调用axios二次封装的login方法,关闭mockjs时会传递到nodejssessionStorage.setItem('accessToken', res.data.token) // 成果返回后放置token到Cookie //console.log("登陆响应",JSON.stringify(res.data));router.push('/home') // 登录成功,跳转到主页}).catch(function(res) {alert(res);});},

3、router/index.js 》再路由配置文件中使用全局前置导航守卫

//全局前置导航守卫,导航跳转前进行token登陆令牌判断router.beforeEach((to, from, next) => {if(to.path === '/login') { next() }else {if(to.meta.requiresAuth && !sessionStorage.getItem('accessToken')) {next({ path: '/login' })}else { //如果不需要登录验证,或者已经登录成功,则直接放行next() } }});

官网API:/zh/guide/advanced/navigation-guards.html

如果觉得《vue-router进阶:路由使用归纳 路由导航守卫 导航守卫解析流程》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。