理解vue2中的computed计算属性与缓存机制

  1. vue实例组件初始化过程中,在执行initState(vm)方法初始化状态时,判断options.computed有值时会进行initComputed$1(vm,options.computed)处理
function initState(vm) {
    let options = vm.$options;
    // 省略props methods data的处理
    // 处理计算属性computed
    if (options.computed) {
       initComputed$1(vm, options.computed)
	}
}
  1. 执行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
    // 每个计算属性创建一个watcher观察者实例
    // lazy:true 默认不执行
    watchers[key] = new Watcher(vm, getter, null, { lazy: true })
    defineComputed(vm, key, userDef);
  }
}
  1. 执行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)
}
  1. 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();
    }
    // 计算属性中的响应式数据依赖收集渲染watcher
    // 保证响应式数据变化时,触发渲染watcher的更新,再触发计算属性的读取get方法
    if (Dep.target) { 
      watcher.depend();
    }
    return watcher.value
  }
}

// 针对computed的简写
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); // 赋值Dep
    let vm = this.vm;
    let value = this.getter.call(vm, vm);
    popTarget();
    this.cleanupDeps();
    return value
  }
  update() {
    if (this.lazy) {
      this.dirty = true;
    }
  }
}
  1. 当计算属性依赖追踪的响应式数据值有变化时,会执行该计算属性对应watcher的update方法,在该方法中会将dirty值置为true
class Watcher {
  ......
  update() {
    if (this.lazy) {
      this.dirty = true;
    }
  }
}
  1. 下次再访问计算属性时,会判断该计算属性对应的watcher实例中的dirty值。如果值为false,表明计算属性依赖追踪的响应式数据未发生变化,则无需进行任何处理,直接拿上一次处理的缓存结果即可。如果值为true,表示追踪的响应式数据有变化,需重新执行watcher.evluate(),更新缓存结果并将新结果返回。
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值