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)    }  }

结尾

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