Vue-Router原始碼分析之install方法

酸楚與甘甜發表於2018-09-07

系列文章:

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方法都做了什麼?

Vue-Router原始碼分析之install方法
程式碼實在是不少,我是真的沒辦法一口氣複製過來,截個圖先給大家做一個展示吧

install 做了那幾件事?

簡單看來: Vue.mixin:侵入式的對每個Vue component進行了擴充套件

Vue-Router原始碼分析之install方法
用ES5的defineProperty的方式設定$router$route屬性。

Vue-Router原始碼分析之install方法
建立了routerView 與RouterLink兩個元件

Vue-Router原始碼分析之install方法
對路由鉤子進行一個統一設定(這塊沒看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面試填坑小分隊

相關文章