由于也是按部就班的理解,对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