Vue-Router源码分析之install方法

3,299 阅读4分钟

系列文章:

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

Vue-Router源码分析之index.js

Vue-Router源码分析之install方法

Vue是怎么注册插件的呢?

使用过Vue的coder都知道,如果想注册一个vue的插件,在vue对象上能够使用的话(并不是绑在Vue.prototype上的那种暴力方式),必须使用Vue.use(你的插件)的方式来注册插件,

用use注册插件需要注意什么呢?

Vue.use方法会寻找插件上的install方法,并且执行,如果插件没有install方法的话,就会报错,无法使用use来注册插件。 哦哟,是真的傲娇的一匹,所以想要使用VueRouter插件的话,就必须拥有一个install方法,

这么一看Vue的插件都需要install方法

所以我们来看看VueRouter的install方法都做了什么?

代码实在是不少,我是真的没办法一口气复制过来,截个图先给大家做一个展示吧

install 做了那几件事?

简单看来: Vue.mixin:侵入式的对每个Vue component进行了扩展

用ES5的defineProperty的方式设置$router$route属性。

创建了routerView 与RouterLink两个组件

对路由钩子进行一个统一设置(这块没看Vue.config.optionMergeStrategies)的源码,目前还不是很熟,当然这不重要了。先往下看

Vue-Router如何使用Vue的内容

大家都知道,Vue.mixin、Vue.component都是Vue类的方法,Vue-Router想要使用的话就必须引入Vue,这是毋庸置疑的,但是如果把Vue打包进入Vue-router的源码内,这必然导致Vue-router的包变得很大,

关键问题!!! 目前没有发现用Vue开发时哪个使用Vue-router开发不提前引入Vue的。

// 声明一个私有的_Vue用来接收外部的Vue类。
export let _Vue
//install方法接收一个参数,也就是Vue类
export function install (Vue) {
  // 如果已经被注册了,并且
  if (install.installed && _Vue === Vue) return
  install.installed = true
  //把Vue类赋值给私有_Vue
  _Vue = Vue

这种方式只需要在install的时候使用全局的Vue类,并不需要将Vue打包进入Vue-router的源码内。

减少不必要的依赖,从我大Vue-router做起。

const isDef = v => v !== undefined
//这是install方法中一个判断值是否为undefined的方法,在接下来会有很多次调用到它,请不要遗漏
// 注册实例的方法
const registerInstance = (vm, callVal) => {
    //vm是什么?就是vue componet实例
    let i = vm.$options._parentVnode
    // 这是一个类似链式调用的方式
    // 目的是确保能确定到this.$options._parentVnode.data.registerRouteInstance是不是存在?
    // 如果找到了那么就自然而然的把i赋值为这个方法,然后执行它
    if (isDef(i) && isDef(i = i.data) && isDef(i = i.registerRouteInstance)) {
      i(vm, callVal)
    }
}

说到这里可能会很乱,我们先留下来这个内容,一会去看看哪里会调用它。

mixin 侵入了哪里?

Vue.mixin({
    beforeCreate () {
      // 只有根节点的$options的属性中有router
      if (isDef(this.$options.router)) {
        this._routerRoot = this
        this._router = this.$options.router
        this._router.init(this)
        Vue.util.defineReactive(this, '_route', this._router.history.current)
      } else {
        this._routerRoot = (this.$parent && this.$parent._routerRoot) || this
      }
      registerInstance(this, this)
    },
    destroyed () {
      registerInstance(this)
    }
  })

看到这里我们应该了解,Vue-Router在install的时候侵入了每个vue componet的beforeCreate以及destroyed生命周期钩子中,在创建之前以及摧毁的时候做一些事情。 做了什么呢? 我们好像看到了registerInstance方法。

// 接受了vue componet自己
registerInstance(this, this)

这个时候重新看一下这个方法,意思就是寻找这个方法去调用一下,this.$options._parentVnode.data.registerRouteInstance(这个方法谁有呢?) 这个我也不知道,我就写在一个子组件内的方法,去查看一下它的父组件们族谱中谁有这个方法。

(function(t){
     while(t){
        if(typeof t.$options._parentVnode.data.registerRouteInstance === 'function'){
            return t
        }else{
            t = t.$parent
        }
    }
        return 'not find'
})(t)

各位看官这是一个很拙劣的方法(勿学)以后会优化这个方法的。 然后发现这个this是谁呢?就是在你使用vue-router时,那个我们对每个组件设置path的那个组件。可以看下面

new vueRouter({
    xxx
    routes : [{
        path : 'xxx',
        component : '某某组件'
    }]
})

每个有这个方法的应该都是这里面设置过的组件,所有我就有一个猜测,是不是路由的改变会导致该路径跳转前后的组件进行生成与摧毁(展示与隐藏)在这些组件创建之前会调用registerInstance方法(这个方法我还没看内部实现)。以后会去验证这个猜测的准确程度。

终于知道这个registerInstance什么时候执行了

router这个属性谁有啊????

 //this.$options.router是绑定在app根节点的组件上。所有只有根组件有这个router属性
 //所以所有的vue component的_routerRoot、_router、都是一样的。
 
 // this.$options.router存在吗?==》 是不是已经绑定万_routerRoot的根节点
 if (isDef(this.$options.router)) {
       // 根节点的_routerRoot就是根节点的vue component
        this._routerRoot = this
        // vue router实例
        this._router = this.$options.router
        // 执行init
        this._router.init(this)
        Vue.util.defineReactive(this, '_route', this._router.history.current)
      } else {
        // 如果没有这个属性 ==》 两种情况 还没绑定这个属性的根节点、不是根节点
        // 未绑定属性的根节点组件,(根节点组件怎么会有爸爸呢)它不存在$parent属性、所以还是指向了自己
        // 不是根节点组件,那就找它爸爸的_routerRoot属性,用它爸爸的
        // vue的子组件beforeCreate肯定晚于父组件beforeCreate所以
        // 所有的组件就像一棵组件树以一样大家,从根向所有树枝树杈去传递这个属性
        // 大家都是用一个属性所以每个组件的_routerRoot都是根节点组件
        this._routerRoot = (this.$parent && this.$parent._routerRoot) || this
      }
      registerInstance(this, this)

所以大家可以尝试的在vue项目中打印出组件本身,查看_routerRoot属性时,每个组件的uid都是一样的,$el也是根节点标签的id 。这不实锤了!!

总结一下

我们可以看出来在vueRouter在注册方面有哪些重要的事?

  • 1:并没有将Vue打包进入Vue-router插件,而是使用外部Vue引入的方式。

  • 2: mixin侵入式的给每个Vue组件的beforeCreate与destroyed生命周期对组件指定路由组件进行了创建与摧毁。

  • 3:所有Vue组件的_router都是同一个Vue-router实例,总之大家都是用一个的。

install方法就介绍到这里,下一节会开始讲述Vue-router的主构造函数。前端er,下一期不见不散

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

个人公众号:IT面试填坑小分队