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对象,实现思路是一样的。

相关推荐
用户516816614584111 小时前
Vue Router 路由懒加载引发的生产页面白屏问题
vue.js·vue-router
前端缘梦11 小时前
Vue Keep-Alive 组件详解:优化性能与保留组件状态的终极指南
前端·vue.js·面试
Simon_He11 小时前
这次来点狠的:用 Vue 3 把 AI 的“碎片 Markdown”渲染得又快又稳(Monaco 实时更新 + Mermaid 渐进绘图)
前端·vue.js·markdown
王同学QaQ16 小时前
Vue3对接UE,通过MQTT完成通讯
javascript·vue.js
华仔啊17 小时前
基于 RuoYi-Vue 轻松实现单用户登录功能,亲测有效
java·vue.js·后端
艾小码17 小时前
告别Vue混入的坑!Composition API让我效率翻倍的3个秘密
前端·javascript·vue.js
Gracemark1 天前
高德地图-地图选择经纬度问题【使用输入提示-使用Autocomplete进行联想输入】(复盘)
vue.js
天下无贼1 天前
【手写组件】 Vue3 + Uniapp 手写一个高颜值日历组件(含跨月补全+今日高亮+选中状态)
前端·vue.js
洋葱头_2 天前
vue3项目不支持低版本的android,如何做兼容
前端·vue.js
奔跑的蜗牛ing2 天前
Vue3 + Element Plus 输入框省略号插件:零侵入式全局解决方案
vue.js·typescript·前端工程化