【Vue】vue中computed源碼詳解


默認computed也是一個watcher,具備緩存,只有當依賴的屬性發生變化才會更新視圖。

原理圖:

流程:computed watcher在defineReactive的get中訂閱屬性的變化(4),在defineReactive的set時觸發notify(4),notify調用每個訂閱了改屬性變化的watcher的update(3),update中將computed watcher 的dirty賦值為true(2),notify中其中一個watcher為渲染watcher,此時執行渲染,渲染時因為模板中使用了computed watcher對應的計算屬性,故而觸發計算屬性的get方法createComputedGetter,在createComputedGetter中調用computed watcher 的evaluate方法(1),evaluate方法調用computed watcher的this.get(5),進而調用用戶在計算屬性上定義的方法進行計算。

源碼:

1、定義computed 的getter方法(在dirty為true時,使用緩存數據)(src\core\instance\state.js):

const computedWatcherOptions = { lazy: true }
function initComputed (vm: Component, computed: Object) {
    ...
    if (!isSSR) {
      // create internal watcher for the computed property.
      watchers[key] = new Watcher(
        vm,
        getter || noop,//將用戶定義傳到watcher中
        noop,
        computedWatcherOptions//lazy:true懶watcher
      )
    }

    // component-defined computed properties are already defined on the
    // component prototype. We only need to define computed properties defined
    // at instantiation here.
    if (!(key in vm)) {
      defineComputed(vm, key, userDef)
    } else if (process.env.NODE_ENV !== 'production') {
      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)
      }
    }
  }
}

export function defineComputed (
  target: any,
  key: string,
  userDef: Object | Function
) {
  const shouldCache = !isServerRendering()
  if (typeof userDef === 'function') {
    sharedPropertyDefinition.get = shouldCache
      ? createComputedGetter(key)//創建計算屬性的getter,不是用用戶傳的
      : createGetterInvoker(userDef)
    sharedPropertyDefinition.set = noop
  } else {
    sharedPropertyDefinition.get = userDef.get
      ? shouldCache && userDef.cache !== false
        ? createComputedGetter(key)
        : createGetterInvoker(userDef.get)
      : noop
    sharedPropertyDefinition.set = userDef.set || noop
  }
  if (process.env.NODE_ENV !== 'production' &&
      sharedPropertyDefinition.set === noop) {
    sharedPropertyDefinition.set = function () {
      warn(
        `Computed property "${key}" was assigned to but it has no setter.`,
        this
      )
    }
  }
  Object.defineProperty(target, key, sharedPropertyDefinition)
}

function createComputedGetter (key) {
  return function computedGetter () {//用戶取值的時候會調用此方法
    const watcher = this._computedWatchers && this._computedWatchers[key]
    if (watcher) {
      if (watcher.dirty) {//dirty為true會去進行求值,這兒的dirty起到了緩存的作用
        watcher.evaluate()
      }
      if (Dep.target) {
        watcher.depend()
      }
      return watcher.value
    }
  }
}

2、computed的getter方法中dirty的賦值時機(src\core\observer\watcher.js):

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

3、update的調用時機(調用當前屬性的訂閱中每一個watcher的update)(src\core\observer\dep.js):

notify () {
    ...
    for (let i = 0, l = subs.length; i < l; i++) {
      subs[i].update()
    }
  }

4、notify的調用時機(src\core\observer\index.js中 defineReactive的set)以及watcher在defineReactive的get訂閱屬性的變化(其中dep.target的賦值是在new Watcher時的this.get):

export function defineReactive (
  obj: Object,
  key: string,
  val: any,
  customSetter?: ?Function,
  shallow?: boolean
) {
  ...
  Object.defineProperty(obj, key, {
    enumerable: true,
    configurable: true,
    get: function reactiveGetter () {
      const value = getter ? getter.call(obj) : val
      if (Dep.target) {
        dep.depend()
        if (childOb) {
          childOb.dep.depend()
          if (Array.isArray(value)) {
            dependArray(value)
          }
        }
      }
      return value
    },
    set: function reactiveSetter (newVal) {
      ...
      dep.notify()
    }
  })
}

dep.target的賦值是在new Watcher時調用this.get(src\core\observer\watcher.js):

constructor (
    vm: Component,
    expOrFn: string | Function,
    cb: Function,
    options?: ?Object,
    isRenderWatcher?: boolean
  ) {
    ...
    this.value = this.lazy
      ? undefined
      : this.get()
  }
get () {
    pushTarget(this)
    let value
    const vm = this.vm
    try {
      value = this.getter.call(vm, vm)
    } catch (e) {
      if (this.user) {
        handleError(e, vm, `getter for watcher "${this.expression}"`)
      } else {
        throw e
      }
    } finally {
      // "touch" every property so they are all tracked as
      // dependencies for deep watching
      if (this.deep) {
        traverse(value)
      }
      popTarget()
      this.cleanupDeps()
    }
    return value
  }

5、使用用戶定義的計算屬性的getter方法計算值(src\core\observer\watcher.js):

get () {
    pushTarget(this)
    let value
    const vm = this.vm
    try {
      value = this.getter.call(vm, vm)
    } catch (e) {
      if (this.user) {
        handleError(e, vm, `getter for watcher "${this.expression}"`)
      } else {
        throw e
      }
    } finally {
      // "touch" every property so they are all tracked as
      // dependencies for deep watching
      if (this.deep) {
        traverse(value)
      }
      popTarget()
      this.cleanupDeps()
    }
    return value
  }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM