7.vue设计与实现-computed的实现(2)

132 阅读2分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路,符合活动条件。

上一篇我们粗糙的实现了 computed的计算功能

computed 函数如下:

function computed(fn){
   
    const effectFn= effect(fn,{
        lazy:true
    })
    return effectFn()
}

代理的对象如下

let obj={
    count:1
}
let newObj=new Proxy(obj,{
    get(){
        ...
    },
    set(){
        ...
    }
})

调用如下:

let result= computed(()=>{
    return newObj.count+1
})
console.log(result)

存在的问题

但是实现的计算功能,有两个问题:

1.每次读取result的时候,会出发effect,不能做到缓存result的值,性能不好

2.修改newObj.count 的值,更新不了result的值

思考

基于第一个问题:如何缓存?

答:第一次获取之后就不再触发effect函数了

如何实现?

答:我们给computed一个状态值dirty,第一次进去是true,true的时候才执行effect,执行之后dirty值修改为false,下次就不会出发effect

基于第二个问题:如何更新result?在什么时候更新?

答:当count值改变的时候,重新执行effect,更新result

如何实现?

答:手动的对result值进行依赖收集,所以我们要重新构建一个对象,进行依赖的收集和依赖的触发,当确定要更新的时候,通过schedule触发

实现

1.实现缓存

function computed(fn){
    let dirty=true
    let value=null
    const effectFn= effect(fn,{
        lazy:true,
      
    })
    const obj={
        get value(){
            if(dirty){
                value=effectFn() 
                dirty=false
            }
            return value
        }
    }
    return obj
}

以上代码通过设置属性访问器的方式,将value值进行缓存。具体逻辑:

1⃣️ 第一次读取result.value的值,dirty为true,第一次触发effectFn函数,读取count,执行track,收集依赖,

2⃣️ 此时由于lazy是true,effect会返回effectFn,此时执行effectFn() 然后将dirty修改为false,由于value值已经被修改了,dirty为false,下次执行的时候,不会再次执行effectFn()

image.png 2.实现count值修改,result值也跟着修改

console.log(result.value)//2
newObj.count=3 
console.log(result.value)//4

实现result.value的值随着count值修改而修改

function computed(fn){
    let dirty=true
    let value=null
    const effectFn= effect(fn,{
        lazy:true,
        //新代码
        schedule:(fn)=>{
            if(!dirty){
                fn()
                dirty=true
            }
        },
      
    })
    const obj={
        get value(){
            if(dirty){
                value=effectFn() 
                dirty=false
            }
            return value
        }
    }
    return obj
}

逻辑如下:

1⃣️ 之前次读取count的值,依赖被收集,然后修改count的值,触发依赖,在schedule中执行count值的更新,然后将dirty修改为true

2⃣️当再次读取result.value的值的时候,再次执行effectFn,读取新的count的值

image.png

总结

1.缓存功能本质上就是执行一次effecFn之后,如果计算的值没有发生改变,不再执行effecFn

2.响应式修改计算属性的功能,本质上就是 在调度函数里面,修改缓存的状态值,使之重新能够执行ffecFn

看到这里,有没有觉得computed 的实现很奇妙呀 😊 😊