• 【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时,使用缓存数据)(srccoreinstancestate.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的赋值时机(srccoreobserverwatcher.js):

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

    3、update的调用时机(调用当前属性的订阅中每一个watcher的update)(srccoreobserverdep.js):

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

    4、notify的调用时机(srccoreobserverindex.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(srccoreobserverwatcher.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方法计算值(srccoreobserverwatcher.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
      }
  • 相关阅读:
    高等软工第三次作业——设计也可以按图索骥
    高等软工第二次作业-从需求分析看软件开发的挑战
    高等软工第一次作业——期望与笃信
    【ACM-ICPC 2018 徐州赛区网络预赛】D.Easy Math 杜教筛
    【HDU 6428】Calculate 莫比乌斯反演+线性筛
    【BZOJ 4199】[Noi2015]品酒大会 后缀自动机+DP
    【BZOJ 3238】差异 后缀自动机+树形DP
    【Codeforces Round #466】E. Cashback DP+ST表
    【BZOJ 4709】柠檬 斜率优化dp+单调栈
    Hello Tornado
  • 原文地址:https://www.cnblogs.com/vickylinj/p/14034645.html
Copyright © 2020-2023  润新知