vue实例组件初始化过程中,在执行initState(vm)方法初始化状态时,判断options.computed有值时会进行initComputed$1(vm,options.computed)处理
function initState ( vm ) { let options = vm. $options; if ( options. computed) { initComputed$1 ( vm, options. computed) }
}
执行initComputed$1方法,每个计算属性都会创建一个watcher观察者实例,用来依赖追踪。然后执行defineComputed方法,把每个计算属性都直接绑定在vm实例上
function initComputed$1 ( vm, computed ) { let watchers = Object. create ( null ) vm. _computedWatchers = watchersfor ( let key in computed) { let userDef = computed[ key] let getter = typeof userDef === 'function' ? userDef : userDef. getwatchers[ key] = new Watcher ( vm, getter, null , { lazy : true } ) defineComputed ( vm, key, userDef) ; }
}
执行defineComputed方法,使用Object.defineProperty()进行数据劫持
let sharedPropertyDefinition = { enumerable : true , configurable : true , get : function ( ) { } , set : function ( ) { }
} ; function defineComputed ( target, key, userDef ) { if ( typeof userDef === 'function' ) { sharedPropertyDefinition. get = createComputedGetter ( key) sharedPropertyDefinition. set = function ( ) { } } else { sharedPropertyDefinition. get = createComputedGetter ( key) sharedPropertyDefinition. set = userDef. set} Object. defineProperty ( target, key, sharedPropertyDefinition)
}
createComputedGetter方法的返回函数是计算属性的get方法,当在模板编译挂载DOM时第一次读取了计算属性,就是触发计算属性的get方法。首先是拿到该计算属性的watcher观察者实例,执行watcher.evluate(),将dirty值置为false,触发计算属性对应watcher的getter方法。在此方法中访问响应式数据时会被响应式数据进行依赖收集,最后将计算属性计算结果进行缓存与返回。
function createComputedGetter ( key ) { return function ( ) { let watcher = this . _computedWatchers && this . _computedWatchers[ key] if ( ! watcher) return if ( watcher. dirty) { watcher. evaluate ( ) ; } if ( Dep. target) { watcher. depend ( ) ; } return watcher. value}
}
class Watcher { constructor ( vm, expOrFn, cb, options ) { this . vm = vmthis . lazy = true this . dirty = this . lazythis . getter = expOrFnthis . value = this . lazy ? undefined : this . get ( ) ; } evaluate ( ) { this . value = this . get ( ) ; this . dirty = false ; } get ( ) { pushTarget ( this ) ; let vm = this . vm; let value = this . getter . call ( vm, vm) ; popTarget ( ) ; this . cleanupDeps ( ) ; return value} update ( ) { if ( this . lazy) { this . dirty = true ; } }
}
当计算属性依赖追踪的响应式数据值有变化时,会执行该计算属性对应watcher的update方法,在该方法中会将dirty值置为true
class Watcher { ... ... update ( ) { if ( this . lazy) { this . dirty = true ; } }
}
下次再访问计算属性时,会判断该计算属性对应的watcher实例中的dirty值。如果值为false,表明计算属性依赖追踪的响应式数据未发生变化,则无需进行任何处理,直接拿上一次处理的缓存结果即可。如果值为true,表示追踪的响应式数据有变化,需重新执行watcher.evluate(),更新缓存结果并将新结果返回。