6.vue设计与实现-computed的实现(1)

534 阅读3分钟

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

上篇我们介绍了 vue设计与实现-调度器

调度器的实现对于我们实现computed有极大的作用,我们这次从使用computed的角度,反推一下computed的实现逻辑。

computed的使用

//我们一般这样使用
const count = ref(1)
const plusOne = computed(() => count.value + 1)

当然,还有第二种对象的方式,我们目前先讨论这种回调函数的方式

反推

我们在computed的回调函数中读取了一个响应式数据:count,然后当我们读取plusOne时,然后将count的值加1 return出去 赋值给到plusOne

结合track和trigger去思考:

读取plusOne时,要读取cout,收集count的依赖函数(这个时候,是会触发一遍effect函数的)

我们大胆的设想一下:如果effect是这样子的:

effect(()=>{
    () => count.value + 1  
})

1.那我们等于是要拿到effect函数执行的返回值,

2.然后将返回值给到用户所定义的属性上面

思路及实现

基于以上两点思考,我们逐一实现一下

实现获取effect函数返回值

我们的effect函数如下,这是未实现effect返回值的代码:

let effect=(fn,option={})=>{
    //清空effectFn 的deps
    const effectFn=()=>{
        cleanUp(effectFn)
        activeEffect=effectFn
        effectStack.push(effectFn)
 
        fn()
        effectStack.pop()
        activeEffect=effectStack[effectStack.length-1]
    }
    effectFn.deps=[]
    effectFn.option=option
    effectFn()
}

我们可以看到,我们第一次读取的时候,执行fn()函数,fn就是 () => count.value + 1 也就是我们要拿到fn的返回值,并且返回出去给到用户使用

修改后的代码如下:

let effect=(fn,option={})=>{
    const effectFn=()=>{
        cleanUp(effectFn)
        activeEffect=effectFn
        effectStack.push(effectFn)
        const res=fn()//这里我们定义了res接收返回值
        effectStack.pop()
        activeEffect=effectStack[effectStack.length-1]
        return res //将res返回出去给用户使用
    }
    effectFn.deps=[]
    effectFn.option=option
    if(option.lazy){
        return effectFn
       
    }else{
        effectFn()
    } 
  
}

实现computed

基于computed 的使用(回调函数的使用),我们可以粗糙的实现一下

function computed(fn){
    const res= effect(fn)
    return res
}

大概是这个鬼样子...

const result= computed(()=>{
return count.value + 1
    })
console.log(result)

我们只需要将要触发的函数传入进入就可以啦

然后执行代码

image.png

为什么获取不到?

一个小bug

注意:我们在computed函数里面返回的是什么?是effect的返回值!

但是我们需要的effect内部effectFn()的返回值,这可咋办?难道我们在effect内部定义一个变量接收effectFn()的返回值,然后返回出去?这样实现的方式不利于管理

不对!我们上篇实现了什么?调度器, 是一个对象包裹着schedule,我们可以在option里面传入一个状态值,去控制effectFn是否返回,是否懒加载

实现effect返回一个effectFn

调用effect

effect(()=>{},{
    lazy:true//懒加载effectFn
})

effect函数修改如下:

let effect=(fn,option={})=>{
    //清空effectFn 的deps
    const effectFn=()=>{
        cleanUp(effectFn)
        activeEffect=effectFn
        effectStack.push(effectFn)
        const res=fn()
        effectStack.pop()
        activeEffect=effectStack[effectStack.length-1]
        return res
    }
    effectFn.deps=[]
    effectFn.option=option
    if(option.lazy){
    //懒加载effectFn
        return effectFn
       
    }else{
        effectFn()
    } 
  
}

通过lazy的值,去控制执行effectFn 还是缓存effectFn,交给用户来选择

这样我们的computed也要做一些修改

修改computed

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

执行computed

const result= computed(()=>{
return count.value + 1
    })
console.log(result)

这样我们就能获取到effectFn的返回值了

image.png 以上,我们便简单的实现了computed 功能,目前实现的功能存在着一些问题,不知道各位小伙伴看出来了没有?

欢迎小伙伴在评论区留言哈~ 😊