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的变化。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 135,099评论 19 139
  • 前言 本文是vue2.x源码分析的第十篇,主要看computed和watch的处理过程! 实例代码 1 compu...
    风之化身呀阅读 1,111评论 0 1
  • 老员工不了解新时代的发展会落伍,新员工业务不熟要锻炼,无论在职场上的哪个位置哪个阶段,都需要不断输入才能保证不掉队...
    卡诺09阅读 564评论 0 0
  • 海南岛自古无冬。霜降之后北方早己进入冰天雪地模式,惟琼岛偏安南海一隅,阻琼州海峡而断雪寒之南下。虽然习惯上我们还是...
    苏察哈尔璨w阅读 409评论 0 5
  • 这几天连续在网上上英语外教一对一的课程,突然觉得自己好像都能听得懂了,信心大增。可昨天那个老师一上来说话特别快,自...
    褶子时光阅读 198评论 1 0