- 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 = watchers
for (let key in computed) {
let userDef = computed[key]
let getter = typeof userDef === 'function' ? userDef : userDef.get
watchers[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 = vm
this.lazy = true
this.dirty = this.lazy
this.getter = expOrFn
this.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(),更新缓存结果并将新结果返回。