系列文章:
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,下一期不見不散