vue3初探三之computed

40 阅读2分钟

先上仓库

用法

传入一个 getter 函数,返回一个默认不可手动修改的 ref 对象。

或者传入一个拥有 get 和 set 函数的对象,创建一个可手动修改的计算状态。

const obj = reactive({
    foo: 'foo',
    bar: 'bar'
})
const plusOne = computed({
    get: () => {
        return obj.foo + obj.bar
    },
     set: (val) => {
         obj.foo = val - 1
     }
})

effect(() => {
    console.log('渲染结果',plusOne.value)
    console.log(plusOne.value)
})
plusOne.value = 3
console.log(obj.foo)

企业微信截图_16765116758519.png

实现分析

取值(plusOne.value)的时候需要执行get函数。那怎么在取值的时候去执行呢,用访问器属性

const test = {
    get value() {
       return 1
    },
    set value(newValue) {
        console.log('设置值', newValue)
    }
}
console.log('test', test.value)
test.value = 3

企业微信截图_16765126271125.png

可见取值的时候会走get,设置值的时候会走get,我们就可以在get的时候去执行computed的get函数

export function computed (getterOrOptions: Function | WritableComputedOptions) {
    let value
    let getter:Function
    let setter:Function
    if (isFunction(getterOrOptions)) {
        getter = getterOrOptions as Function
    } else {
        getter = (getterOrOptions as WritableComputedOptions).get
        setter = (getterOrOptions as WritableComputedOptions).set
    }
    const obj = {
        get value() {
            value = getter()
            return value
        },
        set value(newValue) {
            setter(newValue)
        }
    }
    return obj
}

这样好像没有问题,但是没有缓存,加上缓存试试,get的时候先看数据有没有变化,没有变化直接返回,有变化重新执行get,设置值的时候将缓存标志重置

const obj = {
    get value() {
        if (dirty) { 没有缓存,重新执行getter
            value = getter() 
            dirty = false
        }
        track(obj, 'value')
        return value
    },
    set value(newValue) {
        dirty = true // 设置值的时候重置缓存标志
        setter(newValue)
    }
}

到这里数据倒是正常,但是如果有多个effect函数时,会出现有些effect不渲染,是因为我们在computed里面直接执行的getter,并未与effect做关联,所以在computed里我们不直接执行getter,而是让effect函数返回getter,执行effect的返回函数

const effectFn = effect(getter, {
    lazy: true,
    scheduler() {
        if (!dirty) {
            dirty = true // 通过调度器设置的时候将dirty置为true
            trigger(obj, 'value')// 数据变化时,手动调用trigger触发响应
        }
    }
})
const obj = {
    get value() {
        if (dirty) {
            value = effectFn() // 修改
            dirty = false
        }
        track(obj, 'value') //手动调用track进行追踪
        return value
    },
    set value(newValue) {
        setter(newValue)
    }
}
```