1. 程式人生 > >.4-Vue源碼之數據雙綁(2)

.4-Vue源碼之數據雙綁(2)

font _屬性 def ceo stat urn mark function return

開播了開播了!

  vue通過數據劫持來達到監聽和操作DOM更新,上一節簡述了數組變化是如何監聽的,這一節先講講對象屬性是如何劫持的。

    // Line-855
    Observer.prototype.walk = function walk(obj) {
        var keys = Object.keys(obj);
        for (var i = 0; i < keys.length; i++) {
            // Go!
            defineReactive$$1(obj, keys[i], obj[keys[i]]);
        }
    };

  上一節說到這裏,現在進入defineReactive$$1來看看具體的劫持過程,函數比較長。

  函數接受4個參數,分別為數據對象、鍵、值、默認設置,由於這裏只傳了3個,先不管第4個

    // Line-925
    function defineReactive$$1(
        obj,
        key,
        val,
        customSetter
    ) {
        // 生成一個依賴管理
        var dep = new Dep();
        // getOwnPropertyDescriptor方法以對象形式返回鍵描述信息
// 包括enumerable、writable等等 var property = Object.getOwnPropertyDescriptor(obj, key); // 不可修改的鍵直接返回 if (property && property.configurable === false) { return } // 獲取對應的getter與setter var getter = property && property.get;
var setter = property && property.set; // 值為對象時的嵌套監聽 // 當前值為‘Hello Vue‘的字符串 直接返回 var childOb = observe(val); Object.defineProperty(obj, key, { enumerable: true, configurable: true, get: function reactiveGetter() { // 有getter方法直接調用 var value = getter ? getter.call(obj) : val; // 這裏暫時為null if (Dep.target) { dep.depend(); if (childOb) { childOb.dep.depend(); } if (Array.isArray(value)) { dependArray(value); } } // 直接返回值 return value }, set: function reactiveSetter(newVal) { // 獲取當前值 var value = getter ? getter.call(obj) : val; // 這是比較個啥 只有NaN才會不等於自身吧 if (newVal === value || (newVal !== newVal && value !== value)) { return } // 這個參數沒傳 暫時不知道幹啥的 if ("development" !== ‘production‘ && customSetter) { customSetter(); } // 調用默認setter方法或將新值賦給當前值 if (setter) { setter.call(obj, newVal); } else { val = newVal; } // childOb = observe(newVal); // 廣播變化 dep.notify(); } }); }

  這裏很多方法跳不進去,所以沒法調試看效果,等這個搞完,做數據變化調試的時候再來詳細解釋,目前將就看一下。

  

  由於value只有一個message鍵,所以一次就結束了,函數瘋狂返回,然後回到了observe的構造函數:

    // Line-900
    function observe(value, asRootData) {
        if (!isObject(value)) {
            return
        }
        var ob;
        if (hasOwn(value, ‘__ob__‘) && value.__ob__ instanceof Observer) {
            ob = value.__ob__;
        } else if (
            observerState.shouldConvert &&
            !isServerRendering() &&
            (Array.isArray(value) || isPlainObject(value)) &&
            Object.isExtensible(value) &&
            !value._isVue
        ) {
            // 跑完這裏
            ob = new Observer(value);
        }
        if (asRootData && ob) {
            ob.vmCount++;
        }
        return ob
    }

  

  來看看搞了這麽久,ob是個什麽東西:

  技術分享

  dep是依賴收集數據,包含計數id和依賴數組subs,還有4個原型方法。

  value是被監聽數據,除了數據本身,還添加了__ob__屬性引用自身,自定義了get和set方法,計數的vmCount。

  原型方法包含一個遍歷數組數據的observeArray與監聽對象的walk方法。

  接下來的代碼將vmCount加1,然後返回這個ob對象,返回到了initData函數:

    // Line-3012
    function initData(vm) {
        // ..格式化、代理
        // ...
        // 監聽數據
        observe(data, true /* asRootData */ );
    }

  這個函數也到頭了,返回到了initState函數:

    // Line-2948
    function initState(vm) {
        // ...
        // 從這裏跳了出來
        if (opts.data) {
            initData(vm);
        } else {
            observe(vm._data = {}, true /* asRootData */ );
        }
        // 剩下的兩個參數沒有
        if (opts.computed) {
            initComputed(vm, opts.computed);
        }
        if (opts.watch) {
            initWatch(vm, opts.watch);
        }
    }

  好吧,這個也沒啥執行的,返回到了最原始的_init初始化函數:

    // Line-3924
    Vue.prototype._init = function(options) {
        // ...各種初始化
        // ... 
        // 從這裏跳出來
        initState(vm);
        initProvide(vm); // resolve provide after data/props
        callHook(vm, ‘created‘);

        /* istanbul ignore if */
        if ("development" !== ‘production‘ && config.performance && mark) {
            vm._name = formatComponentName(vm, false);
            mark(endTag);
            measure(((vm._name) + " init"), startTag, endTag);
        }

        if (vm.$options.el) {
            vm.$mount(vm.$options.el);
        }
    };

   接下來是initProvide函數,provide翻譯成中文是準備的意思。

    // Line-3924
    function initProvide(vm) {
        // 沒有這個屬性 跳
        var provide = vm.$options.provide;
        if (provide) {
            vm._provided = typeof provide === ‘function‘ ?
                provide.call(vm) :
                provide;
        }
    }

  然後調用鉤子函數,進入created階段,由於沒有定義執行內容,所以直接跳出來,代碼就不貼了。

  那個dev內容也不太清楚記錄的什麽,暫時先不管,下面是雙綁的另一個大模塊:AST。

  本節先簡單結束了,數據劫持也差不多這些,下一節開始跑節點掛載。

  上圖:

技術分享

.4-Vue源碼之數據雙綁(2)