computed的实现就是利用了Watcher类。它仅有一个数据获取函数,而数据更新时的回调是空函数。

function initComputed (vm, computed) {
  // 一个vue实例所有computed对应的watcher都保存在这
  var watchers = vm._computedWatchers = Object.create(null);

  for (var key in computed) {
    var userDef = computed[key];
    // 确定数据获取函数
    var getter = typeof userDef === 'function' ? userDef : userDef.get;
    {
      if (getter === undefined) {
        warn(
          ("No getter function has been defined for computed property \"" + key + "\"."),
          vm
        );
        getter = noop;
      }
    }

    // 通过watcher实现computed功能
    watchers[key] = new Watcher(vm, getter, noop, computedWatcherOptions);

    if (!(key in vm)) {
      // 让vue实例代理这个computed属性
      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);
      }
    }
  }
}

关于computed对应的watcher的选项有必要多说两句。var computedWatcherOptions = { lazy: true };。这个设定使得dep通知watcher数据变化时,watcher并不立即重新计算相应的值,而是先标记一下当前数据是脏数据,当我们真的需要这个计算属性的时候再去计算(下面代码中evaluate方法)。

// 绑定到vm上
function defineComputed (target, key, userDef) {
  if (typeof userDef === 'function') {
    sharedPropertyDefinition.get = createComputedGetter(key);
    sharedPropertyDefinition.set = noop;
  } else {
    // 计算getter默认是从watcher中获取值
    // 通过设置cache属性控制getter是否强制重算
    sharedPropertyDefinition.get = userDef.get
      ? userDef.cache !== false
        ? createComputedGetter(key)
        : userDef.get
      : noop;

    // 允许设定计算setter
    sharedPropertyDefinition.set = userDef.set
      ? userDef.set
      : noop;
  }
  Object.defineProperty(target, key, sharedPropertyDefinition);
}
function createComputedGetter (key) {
  return function computedGetter () {
    // 注意定义成getter/setter形式时的this指向
    var watcher = this._computedWatchers && this._computedWatchers[key];
    if (watcher) {
      // 标记数据有误,获取当前数据,否则直接返回缓存值
      if (watcher.dirty) {
        // evaluate 方法是lazy watcher专用
        watcher.evaluate();
      }


      if (Dep.target) {
        watcher.depend();
      }

      // computed最终返回的是watcher的值
      return watcher.value
    }
  }
}

computed的每个属性绑定在vm上是通过Object.defineProperty实现的。

最后要说明的是watcher.depend();这句话是干啥的。在讲Watcher的时候,提到过一个嵌套的问题,还是以当时的例子为例,当我们调用完watcherB的evaluate方法后,watcherB的依赖收集已经完成,现在Dep.target上挂的是watcherA,watcherA需要和watcherB所依赖的所有dep实例建立关联,所以这里watcherB调用depend方法,使相应的dep实例与watcherA建立关联。

results matching ""

    No results matching ""