.4-Vue源碼之數據雙綁(2)
阿新 • • 發佈:2017-05-23
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)