Vue-Router源码分析(一): install过程

Vue-router插件install过程。

通常使用vue-router管理SPA 路由时,一定要把router实例挂载到根组件上:

在各个组件中可以直接使用 this.$router this.$route 来访问当前路由信息以及router实例。Vue-Router是怎么做到这一点的?

Vue规定:如果使用Vue插件,则一定要提供install方法,Vue.use方法会把Vue本身传递到插件中,插件作者只需要提供install方法即可获取到Vue类本身。插件可以使用Vue中定义的各种方法,包括prototype中定义的方法。

看下VueRouter install方法做了什么。

js 复制代码
export let _Vue

export function install (Vue) {
  // 防止多次注册
  if (install.installed && _Vue === Vue) return

  // 插件注册标识符
  install.installed = true

  // 保存Vue 类的引用。
  _Vue = Vue

  const isDef = v => v !== undefined

  const registerInstance = (vm, callVal) => {
    let i = vm.$options._parentVnode
    if (isDef(i) && isDef(i = i.data) && isDef(i = i.registerRouteInstance)) {
      i(vm, callVal)
    }
  }

  Vue.mixin({
    beforeCreate () {
      if (isDef(this.$options.router)) { // 根组件
       // _routerRoot 属性指向本身
        this._routerRoot = this
       //  保存用户传入进来的router对象
        this._router = this.$options.router
       // router初始化
        this._router.init(this)
        Vue.util.defineReactive(this, '_route', this._router.history.current)
      } else {
        // 子组件 获取父组件_routerRoot,为了拿到父组件中_router对象
        this._routerRoot = (this.$parent && this.$parent._routerRoot) || this
      }
      registerInstance(this, this)
    },
    destroyed () {
      registerInstance(this)
    }
  })

  Object.defineProperty(Vue.prototype, '$router', {
    get () { return this._routerRoot._router }
  })

  Object.defineProperty(Vue.prototype, '$route', {
    get () { return this._routerRoot._route }
  })

  Vue.component('RouterView', View)
  Vue.component('RouterLink', Link)

  const strats = Vue.config.optionMergeStrategies
  // use the same hook merging strategy for route hooks
  strats.beforeRouteEnter = strats.beforeRouteLeave = strats.beforeRouteUpdate = strats.created
}

第一步: 为了防止多次注册插件,做了一个判断条件:install.installed && _Vue === Vue 当已经注册过该组件并且内部_Vue 已经赋值过,这不进行后续操作。

第二步: 标记组件已经被注册,_Vue保存对传经来的Vue类本身的引用,后续方便其他文件使用Vue中的方法。

第三步: 通过混入的方式,在beforeCreate生命周期函数中,对 <math xmlns="http://www.w3.org/1998/Math/MathML"> o p t i o n s 属性中传入的 r o u t e r 做处理。当程序第一次进入时,一定会走 ' t h i s . options属性中传入的router做处理。 当程序第一次进入时,一定会走`this. </math>options属性中传入的router做处理。当程序第一次进入时,一定会走'this.options.router` 这个判断中,因为根实例中一定会有用户传入的router对象。紧接着在根实例中保存一个变量_routerRoot 对自身的引用,_router 属性则是用户传入进来的router路由信息对象。

这里有个知识点,因为组件是一层层渲染的,beforeCreate钩子函数执行的顺序是:根组件 - > 父组件 - > 子组件 -> 孙组件 ....。 以此下去。所当子孙组件渲染是,执行到beforeCreate 时就会进入else 判断。 else判断中主要做了1件事:

  1. 申明_routerRoot 属性,this.parent && this.parent._routerRoot 这个意思就是如果有父组件并且父组件中有_routerRoot属性,当前子组件中的_routerRoot 就会保存this.$parent._routerRoot,而父组件中的_routerRoot 又是他本身,所有在子组件中_routerRoot 就是父组件自身,如果没有父组件,则_routerRoot属性 就指向子组件this自身。

如果有孙组件,道理一样,那么孙组件一定会走到 else 循环,那么孙组件的_routerRoot 就是指向它父组件的_routerRoot。

所以整个_routerRoot 指向过程是这样:

孙组件 _routerRoot -> 子组件 _routerRoot -> 父组件_routerRoot -> 根组件_routerRoot

第四步:

js 复制代码
  Object.defineProperty(Vue.prototype, '$router', {
    get () { return this._routerRoot._router }
  })

  Object.defineProperty(Vue.prototype, '$route', {
    get () { return this._routerRoot._route }
  })

像Vue原型上注册 <math xmlns="http://www.w3.org/1998/Math/MathML"> r o u t e r 和 router 和 </math>router和route 属性,并只提供一个getter方法。其中返回的是当前组件中父组件的_router 和 _route 属性,这里也可以这么理解,_routerRoot 最终指向的是根组件的_routerRoot,_router 和 _route 也就是 根组件定义的。

到了第四步这里 基本上已经弄清楚 项目中每个Vue组件是怎么获取到 <math xmlns="http://www.w3.org/1998/Math/MathML"> r o u t e r 和 router 和 </math>router和route 了。

第5步:

js 复制代码
  Vue.component('RouterView', View)
  Vue.component('RouterLink', Link)

注册RouterView和RouterLink组件。

其实类推到Vuex中 也是这么一个处理过程,每个组件都能获取到$store对象,实现思路是一样的。

相关推荐
派小汤43 分钟前
Springboot + Vue + WebSocket + Notification实现消息推送功能
vue.js·spring boot·websocket
阿珊和她的猫2 小时前
Webpack Dev Server的安装与配置:解决跨域问题
vue.js·webpack
醋醋3 小时前
Vue2源码记录
前端·vue.js
艾克马斯奎普特3 小时前
Vue.js 3 渐进式实现之响应式系统——第四节:封装 track 和 trigger 函数
javascript·vue.js
敲代码的玉米C3 小时前
Vue Draggable 深入教程:从配置到实现的完整指南
vue.js
frontDeveloper3 小时前
Vue3基础使用概览
vue.js
frontDeveloper3 小时前
Vue2基础原理概览
vue.js
frontDeveloper3 小时前
Vue2基础使用概览
vue.js
JustHappy3 小时前
「我们一起做组件库🌻」虚拟消息队列?message组件有何不同?(VersakitUI开发实录)
前端·javascript·vue.js
Carlos_sam3 小时前
Openlayers:为Overlay创建element的四种方式
前端·javascript·vue.js