由於也是按部就班的理解,對initComputed計算屬性的初始化有幾處看得不是很明白,網上也都是含糊其辭的(要想深刻必須深刻。。。),因此debug了好幾天,纔算是有點頭緒,如今寫出來即幫本身再次理下思路,也可讓大佬指出錯誤 html
首先,基本的雙向綁定原理就不說了,能夠去搜下相關教程,仍是要先理解下簡單的例子vue
function initComputed (vm, computed) { console.log('%cinitComputed','font-size:20px;border:1px solid black') var watchers = vm._computedWatchers = Object.create(null); // computed properties are just getters during SSR var isSSR = isServerRendering(); for (var key in computed) { var userDef = computed[key]; var getter = typeof userDef === 'function' ? userDef : userDef.get; if ("development" !== 'production' && getter == null) { warn( ("Getter is missing for computed property \"" + key + "\"."), vm ); } //minxing---console console.log('%cinitComputed 定義watchers[key]=new Watcher(vm getter noop computedWatcherOptions)','color:white;padding:5px;background:black'); if (!isSSR) { // create internal watcher for the computed property. /** * 熟悉的newWatcher,建立一個訂閱者,爲了以後收集依賴 * 將例子中的num、lastNum和計算屬性comNum進行綁定 * 也就是說在一個deps中有兩個dep,其中的subs分別是 * dep1.subs:[watcher(num),watcher(comNum)] * dep2.subs:[watcher(lastNum),watcher(comNum)] * dep3........ * 請看前面的例子,頁面html中並無渲染{{lastNum}};按理說就不會執行lastNum的getter * 從而就不會和計算屬性進行關聯綁定,若是更改lastNum就不會觸發dep2的notify()發佈 * 天然也就不會在頁面看到comNum有所變化,可是運行後卻不是這樣,爲何呢 * 這就引出這個initComputed的下面方法了---依賴收集(watcher.prototype.depend)! * 當時也是看了很久才知道這個depend方法的做用,後面再說 * 首先先來提個頭,就是下面代碼中watcher中這個getter * 其實就是function comNum() {return this.num+"-computed-"+this.lastNum;}} * 而這個getter何時執行呢,會在Watcher.prototype.evaluate()方法中執行 * 因此watcher中的evaluate()與depend()兩個方法都與initComputed相關 */ watchers[key] = new Watcher( vm, getter || noop, noop, computedWatcherOptions ); } // component-defined computed properties are already defined on the // component prototype. We only need to define computed properties defined // at instantiation here. // 通過判斷後定義計算屬性---(關聯到vm的data上面) if (!(key in vm)) { defineComputed(vm, key, userDef); } else { if (key in vm.$data) { warn(("The computed property \"" + key + "\" is already defined in data."), vm); } else if (vm.$options.props && key in vm.$options.props) { warn(("The computed property \"" + key + "\" is already defined as a prop."), vm); } } } }
這個方法比較簡單主要就是將計算屬性綁定到vm上,重要的下面的createComputedGetter方法數組
function defineComputed ( target, key, userDef ) { console.log('%cdefineComputed','font-size:20px;border:1px solid black') var shouldCache = !isServerRendering(); if (typeof userDef === 'function') { sharedPropertyDefinition.get = shouldCache ? createComputedGetter(key) : userDef; sharedPropertyDefinition.set = noop; } else { sharedPropertyDefinition.get = userDef.get ? shouldCache && userDef.cache !== false ? createComputedGetter(key) : userDef.get : noop; sharedPropertyDefinition.set = userDef.set ? userDef.set : noop; } if ("development" !== 'production' && sharedPropertyDefinition.set === noop) { sharedPropertyDefinition.set = function () { warn( ("Computed property \"" + key + "\" was assigned to but it has no setter."), this ); }; } Object.defineProperty(target, key, sharedPropertyDefinition); }
function createComputedGetter (key) { console.log('createComputedGetter key',key); return function computedGetter () { var watcher = this._computedWatchers && this._computedWatchers[key]; if (watcher) { if (watcher.dirty) { watcher.evaluate(); } if (Dep.target) { watcher.depend(); } return watcher.value } } }
function createComputedGetter (key) { console.log('createComputedGetter key',key); return function computedGetter () { var watcher = this._computedWatchers && this._computedWatchers[key]; if (watcher) { if (watcher.dirty) { console.log('createComputedGetter watcher evaluate==========='); //求值 watcher.evaluate(); } if (Dep.target) { console.log('createComputedGetter watcher depend==========='); //依賴收集 watcher.depend(); } console.log('%ccreateComputedGetter watcher.value is','color:blue;font-size:40px',watcher.value); return watcher.value } } }
爲了更好的說明下,截兩張圖(都是基於最上面的html配置哦)oop
註釋掉watcher.depend()方法,此時deps中沒有dep:id4
其實dep:id4在內存中已經定義好了可是沒有加入到deps中(由於沒有進行依賴收集)
而dep:id5和id6就是上面的數組和遞歸數組中元素的depthis
dep:id3 就是
這下是否是很清楚了lua
進行依賴收集後的depsspa