Vue源碼後記-鉤子函數
vue源碼的馬拉松跑完了,可以放松一下寫點小東西,其實源碼講20節都講不完,跳了好多地方。
本人技術有限,無法跟大神一樣,模擬vue手把手搭建一個MVVM框架,然後再分析原理,只能以門外漢的姿態簡單過一下~
想到什麽寫什麽了,這節就簡單說說鉤子函數吧!
vue中的鉤子函數主要包含初始化的beforeCreated/created,Virtual Dom更新期間的beforeUpdate/updated,頁面渲染期間的beforeMount/mounted,組件銷毀期間的beforeDestroy/destroyed等等。
vue框架會在合適的時間點調用對應的鉤子函數,調用過程其實很簡單,看一下源碼的函數就明白了:
// vm為當前vue實例 // hook為鉤子函數名稱 function callHook(vm, hook) { // 獲取對相應的鉤子函數內容 var handlers = vm.$options[hook]; if (handlers) { for (var i = 0, j = handlers.length; i < j; i++) { try { // 直接調用 handlers[i].call(vm); }catch (e) { handleError(e, vm, (hook + " hook")); } } } // 鉤子函數事件emit if (vm._hasHookEvent) { vm.$emit(‘hook:‘ + hook); } }
其中vm.$option中包含了vm中所有的鉤子函數已經其他一些配置,根據對應的hook字符串取出來然後cal。
可以從一個簡單的例子跑一遍:
<body> <div id=‘app‘> {{message}} </div> </body> <script src=‘./vue.js‘></script> <script> var app = new Vue({ el: ‘#app‘, data: { message: ‘Hello Vue!‘ }, beforeCreate: function() { console.log(‘beforeCreate‘); } }); </script>
樣本案例還是之前跑源碼的,只不過在這裏額外加了一個鉤子函數,隨便選了一個beforeCreate。
跑源碼我寫過,對象會以options對象的形式與baseOptions進行合並處理,如下:
Vue.prototype._init = function() { // code... vm.$options = mergeOptions( resolveConstructorOptions(vm.constructor), options || {}, vm ); // code... }
其options就是new Vue傳進來的那個對象,mergeOptions如下:
function mergeOptions(parent, child, vm) { // code... for (key in child) { if (!hasOwn(parent, key)) { mergeField(key); } } function mergeField(key) { var strat = strats[key] || defaultStrat; options[key] = strat(parent[key], child[key], vm, key); } return options }
其余的屬性都跳過,目前主關註鉤子函數的處理。函數內部會遍歷child(即傳進來的對象),然後根據鍵名取對應strats對象的函數對值進行處理。
可以簡略看一下strats對象:,基本上包含所有可能的鍵,無論是鉤子函數、值、計算屬性、方法等,所有的參數會被處理。
而beforeCreate參數被分類為鉤子函數,會調用mergeHook函數,源碼如下:
// parentVal => undefined // childVal => function(){...} function mergeHook(parentVal, childVal) { return childVal ? parentVal ? parentVal.concat(childVal) : Array.isArray(childVal) ? childVal : [childVal] : parentVal }
這個函數同時也處理props屬性,所以接受兩個參數。如果是鉤子函數,則parentVal為undefined,這個超長三元表達式稍微解說一下:
childVal有沒有?有啊,好,那parentVal呢?沒,那childVal是不是數組啊?不是,是個函數。好,包起來,弄成數組返回吧!
於是,最終鉤子函數被包裝了這樣:
現在,回到最開始那個callHook函數,就能跑得通了。handlers就是這個數組,for循環遍歷,取出函數,調用call方法執行鉤子函數代碼,看情況emit一下,返回。
鉤子函數的作用我就不解釋了,這個網上隨便查都有,我就簡單說說為什麽要在created中調用ajax初始化數據,一起來看看源碼:
function initMixin(Vue) { // code... initLifecycle(vm); initEvents(vm); initRender(vm); callHook(vm, ‘beforeCreate‘); initInjections(vm); // resolve injections before data/props initState(vm); initProvide(vm); // resolve provide after data/props callHook(vm, ‘created‘); // code... }
在beforeCreate階段,vue僅僅完成了參數合並,以及在vm上添加了大量空屬性,並沒有實際開始初始化操作,這時候如果調用ajax,並調用this.data你會發現:
對的,壓根就沒有這個屬性。
但是,在之後的initState初始化中(有興趣可以回頭看下我的流水賬呀),會將data屬性解析並添加到vm上,並且已經有對應的響應式方法了。
所以說,這個時候頁面還沒有渲染,非常適合進行數據初始化。
背不來網上的生命周期,自己畫個圖:
Vue源碼後記-鉤子函數