本文已参与「新人创作礼」活动,一起开启掘金创作之路,符合活动条件。
上一篇我们粗糙的实现了 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()
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的值
总结
1.缓存功能本质上就是执行一次effecFn之后,如果计算的值没有发生改变,不再执行effecFn
2.响应式修改计算属性的功能,本质上就是 在调度函数里面,修改缓存的状态值,使之重新能够执行ffecFn
看到这里,有没有觉得computed 的实现很奇妙呀 😊 😊