计算属性默认不执行,只有当取值的时候才执行
多次取值如果依赖的值不变化,就不会执行
function initComputed(vm, computed) {
// 将 每个计算属性的watchers存起来挂到vm上
const watchers = vm._computedWatchers = {}
for (const key in computed) {
const userDef = computed[key]
// 依赖的属性变化就重新取值
let getter = typeof userDef == 'function' ? userDef : userDef.get
// 每个计算属性本质都是watcher 目前还没用
watchers[key] = new Watcher(vm, getter, () => { }, { lazy: true })
// 将key定义在vm上
defineComputed(vm, key, userDef)
}
}
function createdComputedGetted(key) {
return function () {
let watcher = this._computedWatchers[key];
if(watcher.dirty){
watcher.evaluate();
}
return watcher.value
}
}
function defineComputed(vm, key, userDef) {
let sharedProperty = {}
if (typeof userDef == 'function') {
sharedProperty.get = userDef
}
else {
sharedProperty.get = createdComputedGetted(key)
sharedProperty.set = userDef.set || function () {}
// sharedProperty.set = userDef.set || ()=>()
}
Object.defineProperty(vm, key, sharedProperty)
}
watcher.js文件
// 计算属性走的getter方法
evaluate(){
// 将数据标为不是脏的
this.dirty = false
this.value = this.getters()
}
//当渲染时自动会执行渲染
getters() {
//取值之前先把自己挂载到dep上的静态属性,类似于全局后面变量取值时会将dep和watcher关联
pushTarget(this);
const value = this.exprFunc.call(this.vm);
popTarget();
return value
}
将 每个计算属性的watchers存起来挂到vm上
定义一个dirty标识取得数据是否是脏的,默认为脏的,重写getter方法,
取值时,找到key对应的watcher,判断watcher的dirty是否为true ,
如果是则调用watcher的ecaluate方法,exprFunc方法必须指向当前的vm要不this指向当前watcher,
这样会造成一个问题,这个数据永远都不是脏的了,
在改变属性时,
<li>{{fullname}}</li>
fullname:{
get(){
console.log(111);
return this.age + this.name
}
}
}
这句话的意思是,fullname不会收集渲染watcher 的属性,fullname没有dep手机功能,取值直接去vm上取值, fullname是计算属性的话,方法里面以来的属性会收集计算属性的watcher,
计算属性中的值应该记录计算属性的watcher和渲染watcher
// 判断是否还有Dep.Target
if(Dep.target){
// 如果有将当前watcher对应的deps 在收集当前Dep.target)
watcher.depend()
}
在取值的时候将当前watcher里面对应的deps循环进行收集当前Dep.target)
computed的原理
-
初始化computed的时候,get和set相当于object.defineproprety的get,set,循环遍历computed,并且new watcher,并且将watcher实例存到vm._componentsWatcher[key]对象中,同时有个lazy属性标识watcher内部不默认执行get函数,每个watcher内部会有dity标识,标识数据是不是脏的。
-
将key代理到vm上面,改写劫持的getter函数,获取对应的watcher,判断wathcer.dirty为真,表明数据是脏的,则调用watcher里面的取值方法,取完值将依赖的属性分别收集当前计算属性watcher,当前计算属性watcher收取依赖的属性,取完值之后将状态改为false,这样会造成数据更新了可视图还是不会变,因为计算属性依赖的值没有收集渲染watcher ,所以将当前计算属性watcher里面的deps数组进行循环收集渲染watcher,这样当依赖的属性变了,视图才会更新,重新执行render方法。
-
渲染watcher会在计算属性wathcer之前执行,render执行如果要取计算属性的值,则会先走到改写的getter方法, 取到对应的watcher,然后进行第二部,
-
当依赖的属性变了之后会走run方法,判断如果lazy为true为计算属性的话,会将dity变为true标识数据已经脏了,但是不执行get方法,除了watcher为计算属性的话都执行。
这里比较绕,也是最难的,
这里实现dep对应多个watcher的情况