系列文章:前端
Vue-Router 源碼學習之咱們從API中看些門道vue
使用過Vue的coder都知道,若是想註冊一個vue的插件,在vue對象上可以使用的話(並非綁在Vue.prototype上的那種暴力方式),必須使用Vue.use(你的插件)的方式來註冊插件,vue-router
Vue.use方法會尋找插件上的install方法,而且執行,若是插件沒有install方法的話,就會報錯,沒法使用use來註冊插件。 哦喲,是真的傲嬌的一匹,因此想要使用VueRouter插件的話,就必須擁有一個install方法,app
因此咱們來看看VueRouter的install方法都作了什麼?函數
代碼實在是很多,我是真的沒辦法一口氣複製過來,截個圖先給你們作一個展現吧簡單看來: Vue.mixin:侵入式的對每一個Vue component進行了擴展源碼分析
用ES5的defineProperty的方式設置$router
與
$route
屬性。
建立了routerView 與RouterLink兩個組件
對路由鉤子進行一個統一設置(這塊沒看Vue.config.optionMergeStrategies)的源碼,目前還不是很熟,固然這不重要了。先往下看
你們都知道,Vue.mixin、Vue.component都是Vue類的方法,Vue-Router想要使用的話就必須引入Vue,這是毋庸置疑的,可是若是把Vue打包進入Vue-router的源碼內,這必然致使Vue-router的包變得很大,post
關鍵問題!!! 目前沒有發現用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的源碼內。
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)
}
}
複製代碼
說到這裏可能會很亂,咱們先留下來這個內容,一會去看看哪裏會調用它。
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方法(這個方法我還沒看內部實現)。之後會去驗證這個猜想的準確程度。
//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,下一期不見不散