vue-router 导航守卫中 next 控制实现

更新日期: 2020-05-04阅读: 1.8k标签: 路由

使用 vue-router 的导航守卫钩子函数,某些钩子函数可以让开发者根据业务逻辑,控制是否进行下一步,或者进入到指定的路由。

例如,后台管理页面,会在进入路由前,进行必要登录、权限判断,来决定去往哪个路由,以下是伪代码

// 全局导航守卫
router.beforEach((to, from, next) => {
  if('no login'){
    next('/login')
  }else if('admin') {
    next('/admin')
  }else {
    next()
  }
})

// 路由配置钩子函数
{
  path: '',
  component: component,
  beforeEnter: (to, from, next) => {
    next()
  }
}

// 组件中配置钩子函数
{
  template: '',
  beforeRouteEnter(to, from, next) {
    next()
  }
}

调用 next,意味着继续进行下面的流程;不调用,则直接终止,导致路由中设置的组件无法渲染,会出现页面一片空白的现象。

钩子函数有不同的作用,例如 beforEachafterEachbeforeEnterbeforeRouteEnterbeforeRouteUpdatebeforeRouteLeave,针对这些注册的钩子函数,要依次进行执行,并且在必要环节有控制权决定是否继续进入到下一个钩子函数中。

以下分析下源码中实现的方式,而源码中处理的边界情况比较多,需要抓住核心点,去掉冗余代码,精简出便于理解的实现。

精简源码核心功能

总结下核心点:钩子函数注册的回调函数,能顺序执行,同时会将控制权交给开发者。

先来一个能够注册回调函数的类:

class VueRouter {
  constructor(){
    this.beforeHooks = []
    this.beforeEnterHooks = []

    this.afterHooks = []
  }

  beforEach(callback){
    return registerHook(this.beforeHooks, callback)
  }
  beforeEnter(callback){
    return registerHook(this.beforeEnterHooks, callback)
  }
  afterEach(callback){
    return registerHook(this.afterHooks, callback)
  }
}
function registerHook (list, fn) {
  list.push(fn)
  return () => {
    const i = list.indexOf(fn)
    if (i > -1) list.splice(i, 1)
  }
}

声明的类,提供了 beforEach 、beforeEnter 和 afterEach 来注册必要的回调函数。

抽象出一个 registerHook 公共方法,作用:

  1. 注册回调函数
  2. 返回的函数,可以取消注册的回调函数

使用一下:

const router = new VueRouter()

const beforEach = router.beforEach((to, from, next) => {
  console.log('beforEach');
  next()
})
// 取消注册的函数
beforEach()

以上的回调函数会被取消,意味着不会执行了。


router.beforEach((to, from, next) => {
  console.log('beforEach');
  next()
})

router.beforeEnter((to, from, next) => {
  console.log('beforeEnter');
  next()
})

router.afterEach(() => {
  console.log('afterEach');
})

以上注册的钩子函数会依次执行。beforEach 和 beforeEnter 的回调接收内部传来的参数,同时通过调用 next 可继续走下面的回调函数,如果不调用,则直接被终止了。
最后一个 afterEach 在上面的回调函数都执行后,才被执行,且不接收任何参数。

先来实现依次执行,这是最简单的方式,在类中增加 run 方法,手动调用:


class VueRouter {
  // ... 其他省略,增加 run 函数

  run(){
    // 把需要依次执行的回调存放在一个队列中
    let queue = [].concat(
      this.beforeHooks,
      this.afterHooks 
    )
    
    for(let i = 0; i < queue.length; i++){
      if(queue(i)) {
        queue(i)('to', 'from', () => {})
      }
    }
  }
}

// 手动调用 

router.run()

打印:

'beforEach'
'beforeEnter'

上面把要依次执行的回调函数聚合在一个队列中执行,并传入必要的参数,但这样开发者不能控制是否进行下一步,即便不执行 next 函数,依然会依次执行完队列的函数。

改进一下:

class VueRouter {
  // ... 其他省略,增加 run 函数 

  run(){
    // 把需要依次执行的回调存放在一个队列中
    let queue = [].concat(
      this.beforeHooks,
      this.afterHooks 
    )
    queue[0]('to', 'from', () => {
      queue[1]('to', 'from', () => {
       console.log('调用结束');
      })
    })
  }
}

router.beforEach((to, from, next) => {
  console.log('beforEach');
  // next()
})

router.beforeEnter((to, from, next) => {
  console.log('beforeEnter');
  next()
})

传入的 next 函数会有调用下一个回调函数的行为,把控制权交给了开发者,调用了 next 函数会继续执行下一个回调函数;不调用 next 函数,则终止了队列的执行,所以打印结果是:

'beforEach'

上面实现有个弊端,代码不够灵活,手动一个个调用,在真实场景中无法确定注册了多少个回调函数,所以需要继续抽象成一个功能更强的方法:

function runQueue (queue, fn, cb) {
  const step = index => {
    // 队列执行结束了
    if (index >= queue.length) {
      cb()
    } else {
      // 队列有值
      if (queue[index]) {
        // 传入队列中回调,做一些必要的操作,第二个参数是为了进行下一个回调函数
        fn(queue[index], () => {
          step(index + 1)
        })
      } else {
        step(index + 1)
      }
    }
  }
  // 初次调用,从第一个开始
  step(0)
}

runQueue 就是执行队列的通用方法。

  1. 第一个参数为回调函数队列, 会依次取出来;
  2. 第二个参数是函数,它接受队列中的函数,进行一些其他处理;并能进行下个回调函数的执行;
  3. 第三个参数是队列执行结束后调用。

知道了这个函数的含义,来使用一下:


class VueRouter {
  // ... 其他省略,增加 run 函数 

  run(){
    // 把需要依次执行的回调存放在一个队列中
    let queue = [].concat(
      this.beforeHooks,
      this.beforeEnterHooks 
    )

    // 接收回到函数,和进行下一个的执行函数
    const iterator = (hook, next) => {
      // 传给回调函数的参数,第三个参数是函数,交给开发者调用,调用后进行下一个
      hook('to', 'from', () => {
        console.log('执行下一个回调时,处理一些相关信息');
        next()
      })
    }

    runQueue(queue, iterator, () => {

      console.log('执行结束');
      // 执行 afterEach 中的回调函数
      this.afterHooks.forEach((fn) => {
        fn()
      })
    })
  }
}
// 注册
router.beforEach((to, from, next) => {
  console.log('beforEach');
  next()
})

router.beforeEnter((to, from, next) => {
  console.log('beforeEnter');
  next()
})

router.afterEach(() => {
  console.log('afterEach');
})

router.run();

从上面代码可以看出来,每次把队列 queue 中的回调函数传给 iterator , 用 hook 接收,并调用。
传给 hook 必要的参数,尤其是第三个参数,开发者在注册的回调函数中调用,来控制进行下一步。
在队列执行完毕后,依次执行 afterHooks 的回调函数,不传入任何参数。

所以打印结果为:

beforEach
执行下一个回调时,处理一些相关信息
beforeEnter
执行下一个回调时,处理一些相关信息
执行结束
afterEach

以上实现的非常巧妙,再看 Vue-router 源码这块的实现方式,相信你会豁然开朗。

以上如有偏差欢迎指正学习,谢谢。~~~~

github博客地址:https://github.com/WYseven/blog,欢迎star。

如果对你有帮助,请关注【前端技能解锁】


链接: https://fly63.com/article/detial/9252

vue路由history模式_如何去除vue项目中的#

在使用vue-cli搭建的环境中,浏览器上URL地址中是存在#的,这是由于vue-router 默认 hash 模式,不难发现#的出现真的很丑陋。官网给出了如何使用history模式mode: history

vue路由传参主要的3种方式

vue中路由传参主要的3种方式:query方式(push时使用path来匹配)、params模式(push时使用name来匹配)、location预声明参数模式(push使用path来匹配,但是它跟params模式不同)

vue动态加载路由_实现vue动态加载路由器设置

我们的通用的后台管理系统中,我们会根据权限的粗细不同,会对每个角色每个权限每个资源进行控制。同样的我们也需要实现一个这样的功能。 这篇文章我将主要讲vue端的实现,关于后台接口我就不会涉及,当我接触的时候我们的后台接口是springcloud实现。

两种前端路由的实现方式

前后端分离开发模式,后端会把路由控制丢在前端,这几天再开发单页面小的项目,手动撸了个路由。前端路由实现有两种方法。HTML5 History API包括2个方法:history.pushState()和history.replaceState(),和1个事件:window.onpopstate。hash + location.onhashchange

vue动态路由_vue-router通过接口请求动态生成路由的实现

在后台管理系统中,一般都会采用权限管理。路由菜单数据都会保存到数据库中,在vue-router 2.2版本新增了一个router.addRoutes(routes)方法,即可用它来实现动态路由了

HTML5 History 模式

vue-router 默认 hash 模式 —— 使用 URL 的 hash 来模拟一个完整的 URL,于是当 URL 改变时,页面不会重新加载。如果不想要很丑的 hash,我们可以用路由的 history 模式,这种模式充分利用 history.pushState API 来完成 URL 跳转而无须重新加载页面。

vue router 路由鉴权(非动态路由)

原本想用动态路由的思路去做,按权限加载对应路由表,但是由于权限可以交叉(比如一个人可以同时是主题管理员和数据服务管理员),导致权限路由表还是得去做判断组合。于是放弃了这个思路,索性就在beforeEach里直接判断了。

vue中路由按需加载的几种方式

使用vue-cli构建项目后,我们会在Router文件夹下面的index.js里面引入相关的路由组件,webpack在打包的时候会把整个路由打包成一个js文件,如果页面一多,会导致这个文件非常大,加载缓慢

vue-router 中参数传递(params,query)

query和params的区别,query相当于get请求,在页面跳转的时候,可以在地址栏看到请求参数,然而params则相当于post请求,参数不会在地址栏中显示。

Node.js的路由

当服务端接收到HTTP请求时,可以通过onRequest() 获取到url, pathname,query,及paramParams参数;为了解析这些数据需要使用url和querystring模块

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!