Vue2 源码解析-响应式系统 computed计算属性

71 阅读1分钟

1.初始化阶段

   function initState(vm) {
    vm._watchers = [];
    var opts = vm.$options;
    if (opts.props) {
      initProps(vm, opts.props);
    }
    if (opts.methods) {
      initMethods(vm, opts.methods);
    }
    if (opts.data) {
      initData(vm);
    } else {
      observe((vm._data = {}), true /* asRootData */);
    }
    if (opts.computed) {
      initComputed(vm, opts.computed); <-- 进入computed初始化
    }
    if (opts.watch && opts.watch !== nativeWatch) {
      initWatch(vm, opts.watch);
    }
  }
  
  function initComputed(vm, computed) {
    var watchers = (vm._computedWatchers = Object.create(null)); 
    // 创建一个对象用来存储所有计算属性的watcher
    var isSSR = isServerRendering();
    for (var key in computed) {
      var userDef = computed[key];
      var getter = typeof userDef === "function" ? userDef : userDef.get;
      // 计算属性的两种写法 函数/对象(get/set)
      if (getter == null) {
        warn('Getter is missing for computed property "' + key + '".', vm);
      }
      if (!isSSR) {
        watchers[key] = new Watcher(
          vm,
          getter || noop,
          noop,
          computedWatcherOptions //-> {lazy:true}
        );
        // watcher函数中会执行到 this.value = this.lazy ? undefined : this.get()结束
      }

    // 判断这个计算属性是否已经在组件的原型上定义过,如果已经定义过就不再重复定义。
    // 如果这个计算属性没有在组件的原型上定义过,就调用`defineComputed`函数来
      if (!(key in vm)) {
        defineComputed(vm, key, userDef);
      } else {
        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
          );
        }
      }
      
    

2.响应式实现

     var sharedPropertyDefinition = {
         enumerable: true,
         configurable: true,
         get: noop,
         set: noop,
       };
   function defineComputed(target, key, userDef) {
     var shouldCache = !isServerRendering(); // 是否服务端渲染
     if (typeof userDef === "function") {
     // 如果是函数且不是服务端渲染
       sharedPropertyDefinition.get = shouldCache 
         ? createComputedGetter(key) // 进行缓存处理
         : createGetterInvoker(userDef); // 服务端渲染则无缓存
         //createGetterInvoker -〉 return fn.call(this, this);
         
       sharedPropertyDefinition.set = noop;
     } else {
       sharedPropertyDefinition.get = userDef.get
         ? shouldCache && userDef.cache !== false
           ? createComputedGetter(key)
           : createGetterInvoker(userDef.get)
         : noop;
       sharedPropertyDefinition.set = userDef.set || noop;
     }
     if (sharedPropertyDefinition.set === noop) {
       sharedPropertyDefinition.set = function () {
         warn(
           'Computed property "' +
             key +
             '" was assigned to but it has no setter.',
           this
         );
       };
     }
     Object.defineProperty(target, key, sharedPropertyDefinition);
     // 为计算属性定义getter和setter
   }
   
   
function createComputedGetter(key) {
 return function computedGetter() {
   var watcher = this._computedWatchers && this._computedWatchers[key];
   if (watcher) {
     if (watcher.dirty) { 
     // 实现缓存的关键,关联的data不改变,调用直接return值
     // 只有关联的data属性值改变触发了setter 然后在watcher的update方法里会改变dirty的值为true
       watcher.evaluate();
     }
     if (Dep.target) {
       watcher.depend();
       // 执行完前面的evaluate方法后,Dep.target已经变成了渲染Watcher
       // 执行depend方法后会将渲染Watcher添加到subs中
     }
     return watcher.value; // 直接返回值
   }
 };
}
   // 每次相关数据发生改变 会调用dep.notify方法,subs数组中就有computedWatcher和渲染Watcher
   // computedWatcher调用update方法会改变watcher的dirty为true
   // 渲染watcher 在获取虚拟dom时会获取计算属性然后触发computedGetter
   // 调用evalute方法获取响应式数据改变后的值
Watcher.prototype.update = function update() {
 if (this.lazy) {
   this.dirty = true;
 } 
 ...
}
// 获取到最新值后,设置dirty为false
Watcher.prototype.evaluate = function evaluate() {
 this.value = this.get();
 this.dirty = false;
};