computed

computed选项在initState阶段初始化

function initState(){
	if(opts.computed) {
		initComputed(vm,opts.computed)
	}
}

整个流程图

每个属性创建一个Watcher
首次取值后dirty设置为false
属性添加getter函数并代理到实例上
读取属性时取出Watcher
dirty=true
dirty=false
getter
if (watcher.dirty) watcher.evaluate()
return watcher.value
initComputed
遍历computed
Watcher
lazy = true
dirty = lazy
watcher.evaluate()
watcher.value

initComputed

  1. 为每个computed属性设置一个Watcher
  2. 为属性设置读取访问器getter,并添加到实例上
function initComputed (vm, computed) {
  // 
  var watchers = vm._computedWatchers = Object.create(null)
  // 是否服务端渲染
  var isSSR = isServerRendering();
  // 遍历传入的computed	
  for (var key in computed) {
    var userDef = computed[key];
    // computed可以写成两种格式 function 或者 提供了get方法的对象
    var getter = typeof userDef === 'function' ? userDef : userDef.get;
    if (process.env.NODE_ENV !== 'production' && getter == null) {
      warn(
        ("Getter is missing for computed property \"" + key + "\"."),
        vm
      );
    }
	
    if (!isSSR) {
      // 为计算属性添加一个Watcher
      watchers[key] = new Watcher(
        vm,
        getter || noop,
        noop,
        computedWatcherOptions
      );
    }

    // 实例上不存在时,把它添加到实例上
    if (!(key in vm)) {
      defineComputed(vm, key, userDef);
    } else if (process.env.NODE_ENV !== 'production') {
      // 校验computed重名, data > props > methods	
      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);
      } else if (vm.$options.methods && key in vm.$options.methods) {
        warn(("The computed property \"" + key + "\" is already defined as a method."), vm);
      }
    }
  }
}

Watcher

  • 这里的watcher,设置lazy为true,作用就是新建实例时不会马上读取它的值,同时用于缓存计算结果的标识;
  • 把用户设置的 computed 函数存放到 watcher.getter 中,用于读取属性时取值;
  • watcher.value 存放值
function Watcher (vm,expOrFn,cb,options,isRenderWatcher) {
  this.lazy = options.lazy
  this.dirty = this.lazy; 
  this.getter = expOrFn;  // 用户设置的函数,存放到getter中
  this.value = undefined  //  初始化时不会主动求值
};

defineComputed

为属性设置读取访问器,并添加到实例上

function defineComputed (target,key,userDef) {
   var shouldCache = !isServerRendering();
   sharedPropertyDefinition.get = createComputedGetter(key)
   sharedPropertyDefinition.set = userDef.set || noop;
   Object.defineProperty(target, key, sharedPropertyDefinition);
}

例: 代理到实例上的 computed 属性

vm.key = get function createComputedGetter(){} 

createComputedGetter

这里设置属性的getter返回一个闭包函数, 在读取属性时实际被调用的就是它.在前面的处理过程中已经将该属性对应的Watcher存放到实例的_computedWatchers里, 被调用时从当前实例中取出相应属性的Watcher去求值

// 存入 _computedWatchers 的key
vm._computedWatchers: {
	key1: Watcher,
	key2: Watcher
}
  1. 初次读取属性时 watcher.dirty 为 true, 也就是在上面新建Watcher时,传入lazy的值。现在调用 Watcher.prototype.evaluate 去求值
  2. 再次读取该属性时,由于前一次已经把 dirty 设置为了 false,所以就略过了求值这一步,直接返回了上次保存在watcher.value中的值
function createComputedGetter (key) {
  return function computedGetter () {
    var watcher = this._computedWatchers && this._computedWatchers[key];
    if (watcher) {
      if (watcher.dirty) {
        watcher.evaluate();
      }
      if (Dep.target) {
        watcher.depend();
      }
      return watcher.value
    }
  }
}
Watcher.prototype.evaluate

computed初次被求值后设置 dirty 为 false,后面再读取属性时就不会进入该方法重新求值

Watcher.prototype.evaluate = function() {
  this.value = this.get();
  this.dirty = false;
};
Watcher.prototype.get

this.getter 就是用户传入的computed属性对应的函数, 在上文中有说明. 将当前的Watcher设置为Dep.target以标明当前正在使用的Watcher,函数调用返回的值保存在当前watcher.value

Watcher.prototype.get = function() {
  // 将自身设置到全局位置
  pushTarget(this);
  var value;
  var vm = this.vm;
  // getter 就是computed中用户自己设置的函数,在这里被调用
  value = this.getter.call(vm, vm);
  return value
};

依赖的数据发生变化

在调用函数的过程中,如果访问了data中的数据,则该数据就会收集此Watcher到该数据的dep.subs中
例:

data(){
	return {
		test: "test"
	}
},
computed: {
	showTest(){
		return this.test
	}
},
created(){
	console.log(this.showTest)
}

// 本例在created访问showTest的过程中, 数据test收集了showTest 创建的 Watcher
obj: {
	test: "test",
	__ob__: {
		dep: {
			subs: [Watcher]
		}
	}
}

期间如果test数据发生了变化,该数据在setter中触发notify更新通知

set: function(newVal) {
   dep.notify();
}

取出该数据收集的所有Watcher,依次update

Dep.prototype.notify = function notify () {
  var subs = this.subs.slice(); // dep.subs中存放的是收集自己dep的Watcher
  for (var i = 0, l = subs.length; i < l; i++) {
    subs[i].update();
  }
};

在初始化computed的时候就设置了Watcher.lazy为true, 所以此时再重新设置Watcher.dirty为true,目的就是在下次访问此计算属性时,需要重新求值

Watcher.prototype.update = function update () {
  if (this.lazy) {
    this.dirty = true;
  } 
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值