精读《Vuejs设计与实现》days23

168 阅读3分钟

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变化的时候,手动追踪或触发依赖就可以了。前面几张的铺垫,终于一次性全部用到,这种感觉真的很奇妙。诸君细品