关于javascript:vue-Computed源码学习笔记

40次阅读

共计 2936 个字符,预计需要花费 8 分钟才能阅读完成。

computed 的计算属性有缓存机制,只有当其依赖的响应式数据发生变化时才会清空缓存从新计算结果

 其缓存机制实质是通过一个 dirty 属性管制的,只有 dirty 为 true 时才会从新计算结果替换缓存。dirty 只有当其响应式数据发送变动时才会设置为 true,从新计算后会再次被设置为 false

initComputed:

  1. 遍历咱们定义的 computed 属性,为每一个 computed 属性创立一个外部的监视器 Watcher,保留在 vm 实例的_computedWatchers 中,参数传递了一个 lazy 为 true。
  2. 如果计算属性与已定义的 data 或者 props 中的名称不抵触则会执行 defineComputed 办法。
const computedWatcherOptions = {lazy: true}

function initComputed (vm: Component, computed: Object) {const watchers = vm._computedWatchers = Object.create(null)

  for (const key in computed) {const userDef = computed[key]
    /* 计算属性可能是一个 function,也有可能设置了 get 以及 set 的对象。*/
    let getter = typeof userDef === 'function' ? userDef : userDef.get
    /*
      为计算属性创立一个外部的监视器 Watcher,保留在 vm 实例的_computedWatchers 中
      这里的 computedWatcherOptions 参数传递了一个 lazy 为 true,会使得 watch 实例的 dirty 为 true
    */
    watchers[key] = new Watcher(vm, getter, noop, computedWatcherOptions)

    /* 组件正在定义的计算属性曾经定义在现有组件的原型上则不会进行反复定义 */
    if (!(key in vm)) {
      /* 定义计算属性 */
      defineComputed(vm, key, userDef)
    } else if (process.env.NODE_ENV !== 'production') {
      /* 如果计算属性与已定义的 data 或者 props 中的名称抵触则收回 warning*/
      /* ... */
    }
  }
}

defineComputed:

  1. 通过 createComputedGetter 将每一个 computed 属性包装成了 computedWatcher 实例
  2. 再将每个 computedWatcher 实例通过 Object.defineProperty 再次进行包装,在应用时触发了 get,get 函数会拦挡判断 computedWatcher 实例 dirty 的值,为 true 时则触发 watcher 类的 evaluate 函数,该函数用来获取值最初再敞开 dirty,dirty 是 false 间接返回第一次获取的值。
export function defineComputed (target: any, key: string, userDef: Object | Function) {if (typeof userDef === 'function') {
    /* 创立计算属性的 getter*/
    sharedPropertyDefinition.get = createComputedGetter(key)
    /*
      当 userDef 是一个 function 的时候是不须要 setter 的,所以这边给它设置成了空函数。因为计算属性默认是一个 function,只设置 getter。当须要设置 setter 的时候,会将计算属性设置成一个对象。参考:https://cn.vuejs.org/v2/guide/computed.html# 计算 -setter
    */
    sharedPropertyDefinition.set = noop
  } else {
    /*get 不存在则间接给空函数,如果存在则查看是否有缓存 cache,没有仍旧赋值 get,有的话应用 createComputedGetter 创立 */
    sharedPropertyDefinition.get = userDef.get
      ? userDef.cache !== false
        ? createComputedGetter(key)
        : userDef.get
      : noop
    /* 如果有设置 set 办法则间接应用,否则赋值空函数 */
    sharedPropertyDefinition.set = userDef.set
      ? userDef.set
      : noop
  }
  /*defineProperty 上 getter 与 setter*/
  Object.defineProperty(target, key, sharedPropertyDefinition)
}

/* 创立计算属性的 getter*/
function createComputedGetter (key) {return function computedGetter () {const watcher = this._computedWatchers && this._computedWatchers[key]
    if (watcher) {
      /* 在计算属性中的依赖产生扭转的时候 dirty 会变成 true,在 get 的时候从新计算计算属性的输入值 */
      if (watcher.dirty) {watcher.evaluate()
      }
      /* 依赖收集 */
      if (Dep.target) {watcher.depend()
      }
      return watcher.value
    }
  }
}
/*Watcher 中的 evaluate 函数 */
evaluate () {this.value = this.get()
  this.dirty = false
}

所以 computed 具备缓存的个性,只有当所依赖的数据发生变化,会触发 Object.defineProperty set 里的 notify 办法告诉所有的观察者,notify 又调用 update,update 里如果 lazy 为 true,则会将 dirty 置为 true,一旦 dirty 为 true,计算属性在 get 的时候就会从新计算。

Object.defineProperty(obj, key, {
    enumerable: true,
    configurable: true,
    get: function reactiveGetter () {/* 省略 */},
    set: function reactiveSetter (newVal) {
      /* 省略 */

      /*dep 对象告诉所有的观察者 */
      dep.notify()}
  })

 /* dep/notify */
 notify () {const subs = this.subs.slice()
    for (let i = 0, l = subs.length; i < l; i++) {subs[i].update()}
  }

 /* watcher/update */
 update () {if (this.lazy) {this.dirty = true} else if (this.sync) {this.run()
    } else {queueWatcher(this)
    }
  }

结尾

我是周小羊,一个前端萌新,写文章是为了记录本人日常工作遇到的问题和学习的内容,晋升本人,如果您感觉本文对你有用的话,麻烦点个赞激励一下哟~

正文完
 0