1. 程式人生 > >Vue源碼後記-鉤子函數

Vue源碼後記-鉤子函數

err cto -1 undefined 實例 erro back span error

  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源碼後記-鉤子函數