序言:
序言是有了前两篇的此基础对vue积极响应式基本原理的先期介绍后,再去看这三个小东西会方便快捷许多。写这首诗是为的是三个剖析,除了许多其它的其原因,月底反正。
先看computedcomputed是在initState的这时候,因此在调用data后,展开调用的,来看一看调用的这时候它干了甚么:
function initComputed (vm, computed) { // $flow-disable-line 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 (getter == null) { warn( (“Getter is missing for computed property \”” + key + “\”.”), vm ); } if (!isSSR) { // create internal watcher for the computed property. 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. 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 computed是现阶段的vm模块和示例上的computed特性,说得浅显点是new Vue的这时候写的这个computed,接着我间接高度关注for in 里的标识符:这儿领到computed上的key和相关联的value后,第二步是获得computed里key相关联的value,这儿value可能会是三个表达式,三个第一类,假如是表达式就间接取,假如是第一类就取他的get方式,接着会为当前的key生成三个watcher:
watchers[key] = new Watcher( vm, getter || noop, noop, computedWatcherOptions ); //computedWatcherOptions 等于 var computedWatcherOptions = { lazy: true };
这儿啊,这个lazy假如你对watcher构造表达式的源码模糊了,建议重新打开三个窗口看一看,computed从源码角度来讲,为甚么data里的值变了后才会跟着变呢,是因为这个地方,
第二步是判断现阶段key是否与props或data里边的key重复,有则警告,没有就调用defineComputed:
function defineComputed ( target, key, userDef ) { var shouldCache = !isServerRendering(); if (typeof userDef === function) { sharedPropertyDefinition.get = shouldCache ? createComputedGetter(key) : createGetterInvoker(userDef); sharedPropertyDefinition.set = noop; } else { sharedPropertyDefinition.get = userDef.get ? shouldCache && userDef.cache !== false ? createComputedGetter(key) : createGetterInvoker(userDef.get) : noop; sharedPropertyDefinition.set = userDef.set || noop; } if (sharedPropertyDefinition.set === noop) { sharedPropertyDefinition.set = function () { warn( (“Computed property \”” + key + “\” was assigned to but it has no setter.”), this ); }; } Object.defineProperty(target, key, sharedPropertyDefinition); } //这儿 var sharedPropertyDefinition = { enumerable: true, configurable: true, get: noop, set: noop };
间接看最后一句,vue的老套路了,劫持三个第一类的特性的get set,除了一层作用是执行vm.key的这时候把这个操作代理到了现阶段定义的get上,其实computed的set不怎么常用,那来看一看这儿的get是甚么:shouldChache是isServerRendering的执行结果取反的结果,isServerRendering 字如其名自然跟服务端渲染相关,看源码的话,当在服务端运行的这时候此值才有可能取真值,那这儿他是false,接着,shouldChache自然取true,接着走,接着computed相关联的value一般为function,所以这儿执行createComputedGetter:
function createComputedGetter (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 } } }
这儿实际是把key闭了三个包,返回的表达式呢,是sharedPropertyDefinition的get,所以,此后某个地方访问vm.key的这时候这个闭包表达式就会执行,再看一下这个表达式执行的细节,第二步获得现阶段computed里key相关联的watcher示例,接着判断watcher.dirty是否为true,这个dirty和上边提到的lazy在示例化watcher的这时候用的是同三个值,在调用所有computed的这时候他们都为true,因此第一次渲染的这时候,这个地方会执行到watcher.evaluate():
Watcher.prototype.evaluate = function evaluate () { this.value = this.get(); this.dirty = false; };
evaluate很简单,计算value,标记dirty为false,这儿的get前面我们也应该讲过:他做了一件事情是将现阶段watcher pushTarget,接着调用watcher示例的getter方式,getter方式是调用watcher的这时候传进去的这个getter,那这个getter里的小东西呢,我们注意到computed里一般都会访问到现阶段data的get方式,而前两篇讲了在get方式中,我们就能收集到现阶段的watcher,因此此时就会完成依赖收集。接着computed就定义完毕了。