Vue-Router源码分析之index.js

前言

虽然最近需求着实很多,可是感受本身学习劲头仍是蛮足的,并无被需求压垮。今天,带来Vue-Router源码解析系列的第二篇文章:index.js。html

系列文章:前端

Vue-Router 源码学习之咱们从API中看些门道vue

Vue-Router源码分析之index.jshtml5

Vue-Router源码分析之install方法node

正文

vue-router类里面都作了什么?

index.js是vue-router这个类的主构造函数,因此内容上算是比较关键的:面试

从图片中咱们能够看出来,这是一个ES6声明类的方法,vue-router源码中类的声明都是使用类ES的语法, constructor (options: RouterOptions = {}),在vue-router中使用了flow.js作了类型的检查,

什么是flow.js?flow.js怎么使用呢?由于篇幅缘由,这里就暂时先不作涉及。各位小伙伴,能够参看官网:flow.org/en/docs/typ…ajax

解析:constructor

首先咱们来看一下constructor内的代码,vue-router

constructor (options: RouterOptions = {}) {
    this.app = null
    this.apps = []
    this.options = options
    this.beforeHooks = []
    this.resolveHooks = []
    this.afterHooks = []
    this.matcher = createMatcher(options.routes || [], this)
    //默认为hash锚点
    let mode = options.mode || 'hash'
      //固然使用的是history模式 h5的pushState的方式来实现路由跳转的,对options设置fallback属性为true时会回退到hash模式
      // 是否支持回退
    this.fallback = mode === 'history' && !supportsPushState && options.fallback !== false
    if (this.fallback) {
      mode = 'hash'
    }
    if (!inBrowser) {
      mode = 'abstract'
    }
    //没有fallback的话选择锚点模式,node环境选择abstract模式
    this.mode = mode

    switch (mode) {
      case 'history':
        this.history = new HTML5History(this, options.base)
        break
      case 'hash':
        this.history = new HashHistory(this, options.base, this.fallback)
        break
      case 'abstract':
        this.history = new AbstractHistory(this, options.base)
        break
      default:
        if (process.env.NODE_ENV !== 'production') {
          assert(false, `invalid mode: ${mode}`)
        }
    }
  }
复制代码

咱们声明一个vue-router实例的时候是怎么作的?编程

let router = new Router({
    base : '/',
    mode : 'history',
    routes : [{
        component : xxx,
        path : xxx
    },xxx]
})
复制代码
constructor (options: RouterOptions = {}) 
options就是咱们刚才上面的一个对象,里面有base、mode、routes等属性
复制代码

这时候咱们知道options是个什么东西了,咱们来看看内部对options进行了哪些处理。api

首先咱们说一下vue-router最核心的内容之一:

解析:mode

咱们知到vue-router的路由有两种方式,一种是#锚点性的,第二种是和正常路径同样的,但是vue构建的应用是一个但页面应用如何让他像正常的多页面应用同样,是在不停的改变路径呢? 这里面就使用了html5的history的pushState与replaceState(让页面看起来无刷新的改变路径),具体内容你们能够看一下官网文档和大神张鑫旭的博客(www.zhangxinxu.com/wordpress/2…

在vue-router源码中有一个工具类专门作了这个事情:

咱们来看一下vue-router是如何匹配mode的吧:

// vue-router默认使用hash模式
let mode = options.mode || 'hash';
this.fallback = mode === 'history' && !supportsPushState && options.fallback !== false
// 若是选择了history可是pushState方法并不能使用而且设置了
// 在当浏览器不支持 history.pushState 控制路由是否应该回退到 hash 模式的状况下。
// (options.fallback默认就是true)
// 若是发现须要回退了,就回到hash锚点模式
    if (this.fallback) {
      mode = 'hash'
    }
// 不在浏览器环境就选择abstract模式(在node环境)
    if (!inBrowser) {
      mode = 'abstract'
    }
    this.mode = mode
// 根据三种状况是成不一样的路由转换实例。
// 若是没有mode不是这三种状况就报错。
// HTML5History、HTML5History、HTML5History三个类都是继承与一个base类
// 里面有这三种模式对于路径转换时作的事情进行了必定的封装。
    switch (mode) {
      case 'history':
        this.history = new HTML5History(this, options.base)
        break
      case 'hash':
        this.history = new HTML5History(this, options.base, this.fallback)
        break
      case 'abstract':
        this.history = new HTML5History(this, options.base)
        break
      default:
        if (process.env.NODE_ENV !== 'production') {
          assert(false, `invalid mode: ${mode}`)
        }
    }
复制代码

到这里你们应该对咱们写的mode模式有一点的了解了吧。

解析:init

下面说一下init方法,上一章咱们讲了在根节点的beforeCreate生命周期钩子中,使用了init方法,若是忘记了能够翻看上一篇install方法的学习来回归一下

因此app就是根组件,init在执行前要判断一下,vue-router是否是被vue成功use了,由于成功use以后,会把install方法的installed属性设置为true:

init (app: any /* Vue component instance */) {
    process.env.NODE_ENV !== 'production' && assert(
      install.installed,
      `not installed. Make sure to call \`Vue.use(VueRouter)\` ` +
      `before creating root instance.`
    )
    this.apps.push(app)
    // main app already initialized. (根组件已经被初始化)
    if (this.app) {
      return
    }

    this.app = app

    const history = this.history
    // 当咱们的根组件完成了对vue-router的init的时候咱们就要完成第一次路由的跳转了
    // 当咱们的项目启动的时候确定会有一个路径,这个路径是什么不重要
    // 咱们在第一次进入这个路径的时候,会进行vue-router的初始化
    // 初始化以后要开始展现对应的组件,但是咱们vue-router那些popState的事件确定没有绑定,不会触发啊,
    // 怎么办?? 那就手动触发一下这个事情,
    // 第一次进入确定没有对应的事件,不会完成跳转时该作的事情。
    if (history instanceof HTML5History) {
      history.transitionTo(history.getCurrentLocation())
    } else if (history instanceof HashHistory) {
      const setupHashListener = () => {
        history.setupListeners()
      }
      history.transitionTo(
        history.getCurrentLocation(),
        setupHashListener,
        setupHashListener
      )
    }
    // 针对设置的不一样mode(模式)
    // 将mode的时候,每种模式的history实例来源于三个不一样的类,因此instanceof足够判断是哪一种模式。
复制代码

到如今,咱们上一章的init函数已经串联起来了,不知道你们感受怎么样?我感受舒服了不少了。

路由守卫

用过vue-router的同窗们都知道路由守卫的概念,这是在路由跳转的先后等三个地方设置了不一样的钩子,帮助咱们在进入离开路由前作一些事情。这个钩子是怎么作的呢?

声明了三个数组,存放每一个周期内的钩子上绑定的函数。

vue-router全局级别的beforeEach、beforeResolve、afterEach作了什么?

就这么两行代码你敢信??我也很纠结你就干了这么点事情。 执行一下registerHook函数,从字面意思一看就是注册钩子,

怎么注册的呢?

function registerHook (list: Array<any>, fn: Function): Function {
  list.push(fn)
  // 返回值是一个function
  return () => {
    const i = list.indexOf(fn)
    if (i > -1) list.splice(i, 1)
  }
}
复制代码

接收一个生命周期的钩子数组,将咱们要执行的函数传到数组内就能够完成注册了,我还没看到这三个数组的内容,可是直觉告诉我颇有可能就是,观察者模式(之后就探索去)。注册到这应该就OK了,

为何还有个返回值呢? 返回值的内容一看就是要清楚钩子内的函数呀,咱们调用这个registerHook函数后,能够获得注册函数的清除函数,清除的是钩子数组中对应的函数,还有这么一手,牛的一匹。(让代码教你如何熟练使用闭包~)

vue-router的编程式导航是怎么作的?

push方法与replace、go方法调用对应路由转换实例的对应方法,由于不一样模式你们方法确定都不同, back与forward都是go方法传入特殊参数,因此看到这里咱们发现history这个实例的内容很关键。


清一清嗓

到了这里咱们vue-router的主线流程咱们已经进行了一个梳理,不知道你们对这一块内容感受满意吗? 不满意就请不要邮寄刀片哈。

因此到如今咱们简单进行一下总结

1:mode是设置模式的,有hash、history、abstract三种模式、每一个模式会致使vue-router实例的history不一样,来自三个不一样的类、每一个类又继承于总的base类。 2:init方法会初始化整个组件、而且在vue-router的实例中设置了app属性存放根组件(这个确实颇有用)、手动的完成初始化后的第一次路由跳转。 3:beforeEach、beforeREsolve、afterEach三个全局的钩子都有对应的钩子函数数组,存放每一个周期钩子内要作的事情、使用registerHook方法来完成钩子函数的注册,registerHook也能够清除钩子内对应的函数。 4:push、replace、go等方法都是使用history方法内的对应方法。

总结完毕 咱们学到了什么? history真重要,我要好好看看他内部的实现

真的没出息的总结。

vue-router类中还有一部分对options.routes的处理

options.routes 就是 [{path : 'xxx',components : 'xxx'}] 这个数组
复制代码

生成一个根据options.routes的一份比对程序,完成程序的比对。

下一期的内容要在继续学习index.js和开荒history中进行一个抉择,具体是什么内容你们能够积极留言,给个方向呀。

结束语

每个前端er(boy and girl) 大家都不是一我的在战斗。

安排!!!!

我是一个应届生,最近和朋友们维护了一个公众号,内容是咱们在从应届生过渡到开发这一路所踩过的坑,已经咱们一步步学习的记录,若是感兴趣的朋友能够关注一下,一同加油~

我的公众号:IT面试填坑小分队
相关文章
相关标签/搜索