欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

vue-router源码之history类的浅析

程序员文章站 2023-12-10 18:29:40
当前版本: 3.0.3 类目录: src/history/base.js 前言: 对于vue-router来说,有三种路由模式history,hash,abs...

当前版本: 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() })
          })
        }
      })
    })
  }

结语:

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

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