computed 示例 初始化 computed watcher 实例的构造函数 执行 watcher.depend() 执行 watcher.evaluate() 去求值 computed watcher 依赖的数据变化后会触发 setter 过程,通知所有订阅它变化的 watcher,执行 watcher.update() computed 是什么 computed 实际上是一个 comput…

computed 示例

var vm = new Vue({
  data: {
    firstName: 'Foo',
    lastName: 'Bar'
  },
  computed: {
    fullName: function() {
      return this.firstName + ' ' + this.lastName;
    }
  }
});
  1. 初始化 computed watcher 实例的构造函数
constructor (
  vm: Component,
  expOrFn: string | Function,
  cb: Function,
  options?: ?Object,
  isRenderWatcher?: boolean
) {
  // 针对上例,如果computed存在,不会立即求值,而去建一个Dep实例
  // 当render函数执行访问到this.firstName的时候,就触发的计算属性的getter
  // 这使会拿到计算属性的watcher,然后执行 watcher.depend()
  if (this.computed) {
    this.value = undefined
    this.dep = new Dep()
  } else {
    this.value = this.get()
  }
}
  1. 执行 watcher.depend()
// 这步中Dep.target是渲染watcher
depend () {
  if (this.dep && Dep.target) {
    // 所以this.dep.depend()相当于渲染watcher订阅了整个 computed watcher的变化
    this.dep.depend()
  }
}
  1. 执行 watcher.evaluate() 去求值
// 该步执行完Dep.target就是computed watcher
evaluate () {
  // 判断 this.dirty,如果为true
  if (this.dirty) {
    // 通过 this.get() 求值
    // 求值过程中会执行 value = this.getter.call(vm, vm)
    // this.getter.call(vm, vm)就会执行计算属性的getter函数
    // 在该例子中,就是执行this.firstName + ' ' + this.lastName
    // 因为this.firstNameh和this.lastName是响应式对象,进而就会触发他俩的getter
    // 再进而就把自身持有的dep添加到正在计算的watcher中,这时候Dep.target就是这个 computed watcher
    this.value = this.get()
    // 并且把this.dirty置为false
    this.dirty = false
  }
  // 拿到计算属性对应的值
  return this.value
}
  1. computed watcher 依赖的数据变化后会触发 setter 过程,通知所有订阅它变化的 watcher,执行 watcher.update()
// w
if (this.computed) {
  // 如果没有订阅计算属性变化的watcher,采用lazy模式
  // 只有当下次访问这个计算属性的时候才会重新求值
  if (this.dep.subs.length === 0) {
    this.dirty = true;
  } else {
    // 针对上列,是有订阅计算属性变化的watcher的,所以会执行这步
    this.getAndInvoke(() => {
      this.dep.notify();
    });
  }
} else if (this.sync) {
  this.run();
} else {
  queueWatcher(this);
}
this.getAndInvoke(() => {
  this.dep.notify()
})
// getAndInvoke会对比新旧值,只有数值有变化,才会执行this.dep.notify(),触发渲染watcher重新渲染
getAndInvoke (cb: Function) {
  const value = this.get()
  if (
    value !== this.value ||
    // Deep watchers and watchers on Object/Arrays should fire even
    // when the value is the same, because the value may
    // have mutated.
    isObject(value) ||
    this.deep
  ) {
    const oldValue = this.value
    this.value = value
    this.dirty = false
    if (this.user) {
      try {
        cb.call(this.vm, value, oldValue)
      } catch (e) {
        handleError(e, this.vm, `callback for watcher "${this.expression}"`)
      }
    } else {
      cb.call(this.vm, value, oldValue)
    }
  }
}

computed 是什么

computed 实际上是一个 computed watcher

在初始化 computed 的时候,会触发渲染 watcher 订阅 computed ,然后在求值的过程中,会执行计算属性的 getter 函数,又因为求值的两个变量是响应式对象,所以又触发它俩的 getter,把它俩自身持有 dep 添加到 watcher 中,如果计算属性依赖的变量有变化,会触发渲染 watcher 的重新渲染,如果依赖的变量有变化,但是新值和旧值相等,是不会触发重新渲染,只有最终计算的值发生变化,才会执行 this.dep.notify(),触发渲染 watcher 重新渲染

computed 源码

  1. initComputed
// src/core/instance/state.js
const computedWatcherOptions = { computed: true };
function initComputed(vm: Component, computed: Object) {
  // 先创建vm.computedWatchers为空对象
  const watchers = (vm._computedWatchers = Object.create(null));
  // 判断是否是ssr
  const isSSR = isServerRendering();
  // 然后对computed属性进行遍历
  for (const key in computed) {
    // 拿到计算属性每一个userDef
    const userDef = computed[key];
    // 试着去获取每一个userDef的getter函数
    const getter = typeof userDef === 'function' ? userDef : userDef.get;
    // 拿不到报警告
    if (process.env.NODE_ENV !== 'production' && getter == null) {
      warn(`Getter is missing for computed property "${key}".`, vm);
    }
    // 如果不是ssr,就新建一个Watcher
    if (!isSSR) {
      // 为每一个getter创建一个Watcher
      // 这个Watcher和渲染watcher不同,在代码开头定义了const computedWatcherOptions = { computed: true }
      watchers[key] = new Watcher(vm, getter || noop, noop, computedWatcherOptions);
    }
    // 对key进行判断
    if (!(key in vm)) {
      // 如果不是vm的属性,则用defineComputed
      defineComputed(vm, key, userDef);
    } else if (process.env.NODE_ENV !== 'production') {
      // 如果已经在vm.$data或者在props中,则报出已经存在的警告
      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);
      }
    }
  }
}
  1. defineComputed
// src/core/instance/state.js
// 利用Object.defineProperty给计算属性对应的key值添加getter和setter
// 只要计算属性的key有set,那么setter是计算属性的一个对象,否则就是一个空函数
export function defineComputed(target: any, key: string, userDef: Object | Function) {
  const shouldCache = !isServerRendering();
  if (typeof userDef === 'function') {
    sharedPropertyDefinition.get = shouldCache ? createComputedGetter(key) : userDef;
    sharedPropertyDefinition.set = noop;
  } else {
    sharedPropertyDefinition.get = userDef.get ? (shouldCache && userDef.cache !== false ? createComputedGetter(key) : userDef.get) : noop;
    sharedPropertyDefinition.set = userDef.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);
}
  1. createComputedGetter
// src/core/instance/state.js
function createComputedGetter(key) {
  // 返回一个computedGetter函数,这个就是计算属性的getter
  return function computedGetter() {
    const watcher = this._computedWatchers && this._computedWatchers[key];
    if (watcher) {
      watcher.depend();
      return watcher.evaluate();
    }
  };
}

暂无评论