computed

我们通过下面的例子来看下 computed 属性的实现机制。为了简单起见,我们创建一个没有挂载节点的Vue实例。

var example = new Vue({
      data: {
        a: 1
      },
      computed: {
        b: function () {
          return this.a + 1
        }
      }
    })

    example.a++
    console.log(example.b) // -> 3
    example.a = 5
    console.log(example.b) // -> 6

data属性

computed属性中的方法和data属性直接相关,data对象的字段都会经过如下方法实现响应式:

function defineReactive (obj, key, val) {
  var dep = new Dep() // dep对象负责维护订阅列表(对数据而言,就是Watcher对象)

  // cater for pre-defined getter/setters
  var getter, setter
  if (config.convertAllProperties) {
    var property = Object.getOwnPropertyDescriptor(obj, key)
    if (property && property.configurable === false) {
      return
    }
    getter = property && property.get
    setter = property && property.set
  }

  var childOb = observe(val)
  Object.defineProperty(obj, key, {
    enumerable: true,
    configurable: true,
    get: function reactiveGetter () {
      var value = getter ? getter.call(obj) : val
      if (Dep.target) {  // Dep.target是个Watcher对象实例
        dep.depend()  // 把Dep.target这个Watcher对象加入到dep的subs列表中
        if (childOb) {
          childOb.dep.depend()
        }
        if (isArray(value)) {
          for (var e, i = 0, l = value.length; i < l; i++) {
            e = value[i]
            e && e.__ob__ && e.__ob__.dep.depend()
          }
        }
      }
      return value
    },
    set: function reactiveSetter (newVal) {
      var value = getter ? getter.call(obj) : val
      if (newVal === value) {
        return
      }
      if (setter) {
        setter.call(obj, newVal)
      } else {
        val = newVal
      }
      childOb = observe(newVal)
      // 此处会调用依赖列表的watcher进行数据视图的同步! 
      // watcher会调用directive的更新方法
      dep.notify()
    }
  })
}

computed属性

初始化时,computed属性的主要方法如下:

Vue.prototype._initComputed = function () {
    var computed = this.$options.computed
    if (computed) {
      for (var key in computed) {
        var userDef = computed[key]
        var def = {
          enumerable: true,
          configurable: true
        }
        if (typeof userDef === 'function') {
          def.get = makeComputedGetter(userDef, this)
          def.set = noop
        } else {
          def.get = userDef.get
            ? userDef.cache !== false
              ? makeComputedGetter(userDef.get, this)
              : bind(userDef.get, this)
            : noop
          def.set = userDef.set
            ? bind(userDef.set, this)
            : noop
        }
        Object.defineProperty(this, key, def)
      }
    }
  }

  function makeComputedGetter (getter, owner) {
    var watcher = new Watcher(owner, getter, null, {
      lazy: true
    })
    return function computedGetter () {
      if (watcher.dirty) {
        watcher.evaluate()
      }
      if (Dep.target) {
        watcher.depend()
      }
      return watcher.value
    }
  }

关联

第一次获取computed属性中的字段的值时,会因为watcher的lazy为true,执行一次evaluate方法,而且会把watcher加入到data字段的订阅者列表中。

DOM节点中有和computed属性相关的字段,会在调用get方法时,通过computed中的getter方法完字段取值、依赖搜集。

如果对computed中的字段直接设值,并不能引起View的变化。

你可能感兴趣的:(computed)