Vue-Router源码分析之index.js

栏目: 编程工具 · 发布时间: 7年前

内容简介:上一篇我们聊了:虽然最近需求着实不少,但是感觉自己学习劲头还是蛮足的,并没有被需求压垮。今天,带来Vue-Router源码解析系列的第二篇文章:index.js。index.js是vue-router这个类的主构造函数,所以内容上算是比较关键的:

上一篇我们聊了: Vue-Router源码学习之install方法

虽然最近需求着实不少,但是感觉自己学习劲头还是蛮足的,并没有被需求压垮。今天,带来Vue-Router源码解析系列的第二篇文章:index.js。

正文

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

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

Vue-Router源码分析之index.js
从图片中我们可以看出来,这是一个ES6声明类的方法,vue-router源码中类的声明都是使用类ES的语法, constructor (options: RouterOptions = {})

,在vue-router中使用了flow.js做了类型的检查,

什么是flow.js?flow.js怎么使用呢?因为篇幅原因,这里就暂时先不做涉及。各位小伙伴,可以参看官网: flow.org/en/docs/typ…

解析:constructor

首先我们来看一下constructor内的代码,

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进行了哪些处理。

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

解析:mode

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

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

Vue-Router源码分析之index.js

我们来看一下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:

Vue-Router源码分析之index.js
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源码分析之index.js

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

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

Vue-Router源码分析之index.js

就这么两行代码你敢信??我也很纠结你就干了这么点事情。 执行一下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的编程式导航是怎么做的?

Vue-Router源码分析之index.js

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) 你们都不是一个人在战斗。

安排!!!!

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

Vue-Router源码分析之index.js

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

On LISP

On LISP

Paul Graham / Prentice Hall / 09 September, 1993 / $52.00

On Lisp is a comprehensive study of advanced Lisp techniques, with bottom-up programming as the unifying theme. It gives the first complete description of macros and macro applications. The book also ......一起来看看 《On LISP》 这本书的介绍吧!

SHA 加密
SHA 加密

SHA 加密工具

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具