vue3 computed 探究 —— 组件

65 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第6天,点击查看活动详情

接着上篇,我们再来看另一个例子:

<template> 
    <div> 
        {{ plusOne }} 
    </div> 
    <button @click="plus">plus</button> 
</template> 
<script> 
    import { ref, computed } from 'vue' 
    export default { 
        setup() { 
            const count = ref(0) 
            const plusOne = computed(() => { 
                return count.value + 1 
            }) 
            function plus() { 
                count.value++ 
            } 
            return { 
                plusOne, 
                plus 
            } 
        } 
    } 
</script>

可以看到,在这个例子中我们利用 computed API 创建了计算属性对象 plusOne,它传入的是一个 getter 函数,为了和后面计算属性对象的 getter 函数区分,我们把它称作 computed getter。另外,组件模板中引用了 plusOne 变量和 plus 函数。

组件渲染阶段会访问 plusOne,也就触发了 plusOne 对象的 getter 函数:

get value() { 
    // 计算属性的 getter 
    if (dirty) { 
        // 只有数据为脏的时候才会重新计算 
        value = runner() 
        dirty = false 
    } 
    // 依赖收集,收集运行访问该计算属性的 activeEffect 
    track(computed, "get" /* GET */, 'value') 
    return value 
}

由于默认 dirty 是 true,所以这个时候会执行 runner 函数,并进一步执行 computed getter,也就是 count.value + 1,因为访问了 count 的值,并且由于 count 也是一个响应式对象,所以就会触发 count 对象的依赖收集过程。

请注意,由于是在 runner 执行的时候访问 count,所以这个时候的 activeEffect 是 runner 函数。runner 函数执行完毕,会把 dirty 设置为 false,并进一步执行 track(computed,"get",'value') 函数做依赖收集,这个时候 runner 已经执行完了,所以 activeEffect 是组件副作用渲染函数。

所以你要特别注意这是两个依赖收集过程:对于 plusOne 来说,它收集的依赖是组件副作用渲染函数;对于 count 来说,它收集的依赖是 plusOne 内部的 runner 函数。

然后当我们点击按钮的时候,会执行 plus 函数,函数内部通过 count.value++ 修改 count 的值,并派发通知。请注意,这里不是直接调用 runner 函数,而是把 runner 作为参数去执行 scheduler 函数。我们来回顾一下 trigger 函数内部对于 effect 函数的执行方式:

const run = (effect) => { 
    // 调度执行 
    if (effect.options.scheduler) { 
        effect.options.scheduler(effect) 
    } 
    else { 
        // 直接运行 
        effect() 
    } 
}

computed API 内部创建副作用函数时,已经配置了 scheduler 函数:

scheduler: () => { 
    if (!dirty) { 
        dirty = true 
        // 派发通知,通知运行访问该计算属性的 activeEffect 
        trigger(computed, "set" /* SET */, 'value') 
    } 
}

它并没有对计算属性求新值,而仅仅是把 dirty 设置为 true,再执行 trigger(computed, "set" , 'value'),去通知执行 plusOne 依赖的组件渲染副作用函数,即触发组件的重新渲染。

在组件重新渲染的时候,会再次访问 plusOne,我们发现这个时候 dirty 为 true,然后会再次执行 computed getter,此时才会执行 count.value + 1 求得新值。这就是虽然组件没有直接访问 count,但是当我们修改 count 的值的时候,组件仍然会重新渲染的原因。