4.8 计算属性computed 与 lazy
上一篇文章讲完了lazy,利用这个lazy,我们就可以实现一个简单的computed。
首先,定义一下computed的基本实现,它是一个函数,传入参数也是函数.
我们在里面调用之前写的effect,并且使用lazy
同时,我们定义只有在读取这个值的时候再执行副作用函数
const computed = (getter) => {
const effectFn = effect(getter, { lazy: true } )
const obj = {
get value(){
return effectFn()
}
set value(){
console.log('computed cannot change')
}
}
return obj
}
这样就简单的实现了computed的惰性计算,当直访问xxx.value时才执行,但是computed还有一个重要的特征:缓存结果。
想象一下,我们上面这个computed函数,这样显然时性能上的浪费,可能我们执行100次,这个值都没变,那为什么要执行呢?
xxx.value //执行一次副作用函数
xxx.value //再执行一次副作用函数
xxx.value //再执行一次副作用函数
我们首先在,computed中新增2个变量,dirty,value。其中,value用来存储上一次的返回结果,而dirty则用来判断是否需要执行副作用函数。那么我们就可以这样写。
const computed = (getter) => {
let value = null //新增
let dirty = true //新增
const effectFn = effect(getter, { lazy: true } )
const obj = {
get value(){
if(dirty){ //新增
value = effectFn()
dirty = false
}
return value
}
set value(){
console.log('computed cannot change')
}
}
return obj
}
这样就是实现了基本的缓存结果,但是我们测试后会发现新的问题,每次xxx.value的时候,把dirty重置了,那么下一次就不会调用副作用函数,如果你的副作用函数中同时依赖了2个变量,那么一个变量没变,整个值都不会变了。
因此,我们需要一个调度器
const computed = (getter) => {
let value = null
let dirty = true
const effectFn = effect(getter, {
lazy: true,
scheduler(){
dirty = true //新增
}
} )
const obj = {
get value(){
if(dirty){
value = effectFn()
dirty = false
}
return value
}
set value(){
console.log('computed cannot change')
}
}
return obj
}
当我看到这里的时候,我震惊了,前面几张过于分散的知识点,让我摸不到头脑,但是看到这段的时候,我才知道,前面已经把砖都准备好了,现在已经开始盖房。把大象放进冰箱,也就是这么简单了。图穷匕见,仅余震惊。
之前的文章里有些到effect嵌套,现在就遇到了,如果你在computed中引用另一个computed,这不就是effect嵌套吗?
在我们之前也有解决过来,我这里就直接写代码了,因为之前已经封装过track,trigger了
const computed = (getter) => {
let value = null
let dirty = true
const effectFn = effect(getter, {
lazy: true,
scheduler(){
if(!dirty){
dirty = true
//这里触发一下
trigger(obj,'value')
}
}
} )
const obj = {
get value(){
if(dirty){
value = effectFn()
dirty = false
}
//在这里追踪依赖
track(obj,'value')
return value
}
set value(){
console.log('computed cannot change')
}
}
return obj
}
如代码所示,我们只需要在dirty变化的时候,手动追踪或触发依赖就可以了。前面几张的铺垫,终于一次性全部用到,这种感觉真的很奇妙。诸君细品