vue-router源码之history类的浅析


Posted in Javascript onMay 21, 2019

当前版本: 3.0.3

类目录: src/history/base.js

前言:

对于vue-router来说,有三种路由模式history,hash,abstract, abstract是运行在没有window的环境下的,这三种模式都是继承于history类,history实现了一些共用的方法,对于一开始看vue-router源码来说,可以从这里开始看起。

初始属性

router: Router; 表示VueRouter实例。实例化History类时的第一个参数
 base: string;  表示基路径。会用normalizeBase进行规范化。实例化History类时的第二个参数。
 current: Route; 表示当前路由(route)。
 pending: ?Route; 描述阻塞状态。
 cb: (r: Route) => void; 监听时的回调函数。
 ready: boolean; 描述就绪状态。
 readyCbs: Array<Function>; 就绪状态的回调数组。
 readyErrorCbs: Array<Function>; 就绪时产生错误的回调数组。
 errorCbs: Array<Function>; 错误的回调数组

 // implemented by sub-classes
 <!-- 下面几个是需要子类实现的方法,这里就先不说了,之后写其他类实现的时候分析 -->
 +go: (n: number) => void;
 +push: (loc: RawLocation) => void;
 +replace: (loc: RawLocation) => void;
 +ensureURL: (push?: boolean) => void;
 +getCurrentLocation: () => string;

对于history类来说,主要是下下面两个函数的逻辑

transitionTo

这个方法主要是对路由跳转的封装, location接收的是HTML5History,HashHistory,AbstractHistory, onComplete是成功的回调,onAbort是失败的回调

transitionTo (location: RawLocation, onComplete?: Function, onAbort?: Function) {
  const route = this.router.match(location, this.current) // 解析成每一个location需要的route
  this.confirmTransition(route, () => {
   this.updateRoute(route)
   onComplete && onComplete(route)
   this.ensureURL()

   // fire ready cbs once
   if (!this.ready) {
    this.ready = true
    this.readyCbs.forEach(cb => { cb(route) })
   }
  }, err => {
   if (onAbort) {
    onAbort(err)
   }
   if (err && !this.ready) {
    this.ready = true
    this.readyErrorCbs.forEach(cb => { cb(err) })
   }
  })
 }

confirmTransition

这是方法是确认跳转,route是匹配的路由对象, onComplete是匹配成功的回调, 是匹配失败的回调

confirmTransition(route: Route, onComplete: Function, onAbort?: Function) {
    const current = this.current
    const abort = err => { // 异常处理函数
      if (isError(err)) {
        if (this.errorCbs.length) {
          this.errorCbs.forEach(cb => { cb(err) })
        } else {
          warn(false, 'uncaught error during route navigation:')
          console.error(err)
        }
      }
      onAbort && onAbort(err)
    }
    if (
      isSameRoute(route, current) &&
      // in the case the route map has been dynamically appended to
      route.matched.length === current.matched.length
    ) {
      this.ensureURL()
      return abort()
    }
    <!-- 根据当前路由对象和匹配的路由:返回更新的路由、激活的路由、停用的路由 -->
    const {
      updated,
      deactivated,
      activated
    } = resolveQueue(this.current.matched, route.matched)
    <!-- 需要执行的任务队列 -->
    const queue: Array<?NavigationGuard> = [].concat(
      // beforeRouteLeave 钩子函数
      extractLeaveGuards(deactivated),
      // 全局的beforeHooks勾子
      this.router.beforeHooks,
      // beforeRouteUpdate 钩子函数调用
      extractUpdateHooks(updated),
      // config里的勾子
      activated.map(m => m.beforeEnter),
      // async components
      resolveAsyncComponents(activated)
    )
    
    this.pending = route
    <!-- 对于queue数组所执行的迭代器方法 -->
    const iterator = (hook: NavigationGuard, next) => {
      if (this.pending !== route) {
        return abort()
      }
      try {
        hook(route, current, (to: any) => {
          if (to === false || isError(to)) {
            // next(false) -> abort navigation, ensure current URL
            this.ensureURL(true)
            abort(to)
          } else if (
            typeof to === 'string' ||
            (typeof to === 'object' && (
              typeof to.path === 'string' ||
              typeof to.name === 'string'
            ))
          ) {
            // next('/') or next({ path: '/' }) -> redirect
            abort()
            if (typeof to === 'object' && to.replace) {
              this.replace(to)
            } else {
              this.push(to)
            }
          } else {
            // confirm transition and pass on the value
            next(to)
          }
        })
      } catch (e) {
        abort(e)
      }
    }
    
    runQueue(queue, iterator, () => {
      const postEnterCbs = []
      const isValid = () => this.current === route
      <!-- beforeRouteEnter 钩子函数调用 -->
      const enterGuards = extractEnterGuards(activated, postEnterCbs, isValid)
      const queue = enterGuards.concat(this.router.resolveHooks)
      <!-- 迭代运行queue -->
      runQueue(queue, iterator, () => {
        if (this.pending !== route) {
          return abort()
        }
        this.pending = null
        onComplete(route)
        if (this.router.app) {
          this.router.app.$nextTick(() => {
            postEnterCbs.forEach(cb => { cb() })
          })
        }
      })
    })
  }

结语:

每一次总结,都是对之前读源码的再一次深入的了解

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
jQuery JSON的解析方式分享
Apr 05 Javascript
Js 冒泡事件阻止实现代码
Jan 27 Javascript
js类式继承的具体实现方法
Dec 31 Javascript
javascript使用call调用微信API
Dec 15 Javascript
React简单介绍
May 24 Javascript
利用Ionic2 + angular4实现一个地区选择组件
Jul 27 Javascript
详解JS浏览器事件循环机制
Mar 27 Javascript
js实现继承的方法及优缺点总结
May 08 Javascript
jQuery实现高级检索功能
May 28 jQuery
写给新手同学的vuex快速上手指北小结
Apr 14 Javascript
jquery实现简易验证插件封装
Sep 13 jQuery
解决await在forEach中不起作用的问题
Feb 25 Javascript
vue 地图可视化 maptalks 篇实例代码详解
May 21 #Javascript
vue 中使用 watch 出现了如下的报错的原因分析
May 21 #Javascript
Node.js 获取微信JS-SDK CONFIG的方法示例
May 21 #Javascript
vue+element创建动态的form表单及动态生成表格的行和列
May 20 #Javascript
Node 搭建一个静态资源服务器的实现
May 20 #Javascript
vue+element实现打印页面功能
May 20 #Javascript
vue+element实现表单校验功能
May 20 #Javascript
You might like
PHP实现对png图像进行缩放的方法(支持透明背景)
2015/07/15 PHP
php版微信公众平台实现预约提交后发送email的方法
2016/09/26 PHP
jquery下实现overlay遮罩层代码
2010/08/25 Javascript
超越Jquery_01_isPlainObject分析与重构
2010/10/20 Javascript
js相册效果代码(点击创建即可)
2013/04/16 Javascript
javascript实现的平方米、亩、公顷单位换算小程序
2014/08/11 Javascript
nodejs npm package.json中文文档
2014/09/04 NodeJs
使用Raygun对Node.js应用进行错误处理的方法
2015/06/23 Javascript
使用jQuery实现鼠标点击左右按钮滑动切换
2017/08/04 jQuery
JavaScript canvas实现围绕旋转动画
2017/11/18 Javascript
React Native自定义控件底部抽屉菜单的示例
2018/02/08 Javascript
WebSocket的通信过程与实现方法详解
2018/04/29 Javascript
Node.js折腾记一:读指定文件夹,输出该文件夹的文件树详解
2019/04/20 Javascript
微信小程序登录数据解密及状态维持实例详解
2019/05/06 Javascript
pm2启动ssr失败的解决方法
2019/06/29 Javascript
VUE路由动态加载实例代码讲解
2019/08/26 Javascript
JS FormData对象使用方法实例详解
2020/02/12 Javascript
[42:36]DOTA2上海特级锦标赛B组败者赛 VG VS Spirit第二局
2016/02/26 DOTA
Python与R语言的简要对比
2017/11/14 Python
Python实现XML文件解析的示例代码
2018/02/05 Python
钉钉群自定义机器人消息Python封装的实例
2019/02/20 Python
python导包的几种方法(自定义包的生成以及导入详解)
2019/07/15 Python
python文字转语音的实例代码分析
2019/11/12 Python
tensorflow 分类损失函数使用小记
2020/02/18 Python
django filter过滤器实现显示某个类型指定字段不同值方式
2020/07/16 Python
python中numpy数组与list相互转换实例方法
2021/01/29 Python
家用个人磨皮机:Trophy Skin
2017/03/30 全球购物
几个Shell Script面试题
2012/08/31 面试题
高中生学习生活的自我评价
2013/10/09 职场文书
英语专业学生个人求职信范文
2014/01/06 职场文书
幼儿园新学期寄语
2014/01/18 职场文书
优乐美广告词
2014/03/14 职场文书
安全员岗位职责
2015/02/10 职场文书
初一英语教学反思
2016/02/15 职场文书
JS代码编译器Monaco使用方法
2021/06/11 Javascript
Ruby序列化和持久化存储 Marshal和Pstore介绍
2022/04/18 Ruby