Vue3基础学习——计算属性

笔记 · 17 天前 · 37 人浏览

计算属性

用来描述依赖响应式状态的复杂逻辑,

概念

“懒计算”机制,只有在被访问时才会重新计算。接受一个getter函数并返回一个只读响应式ComputedRef对象。
computed()默认假设传入的就是getter函数。

和方法相比

计算属性值有缓存和懒计算机制:

  • 只有访问计算属性的值时才,getter函数才会首次执行
  • 计算结果会被缓存,只有当依赖(如count.value)发生变化时,缓存才会失效
  • 依赖变化后,下次访问时getter会重新运行并更新缓存

缓存的运行原理

  1. 初始化与缓存结构:
    在定义一个计算属性时,computed()会创建一个ComputedRef对象,在其内部维护两个关键状态:

    • value:存储计算结果,初始未定义,计算尚未发生
    • dirty:布尔值,表示缓存是否有效(true表示缓存有效,false表示缓存无效),初始值为true
  2. 首次计算:
    第一次访问.value时:

    • 检查dirty是否为true
    • 执行getter函数

      • 访问依赖的响应式状态(如count.value),触发依赖收集(track),将响应式状态作为依赖
      • 计算结果存储在value
    • dirty设置为false,表示缓存已更新
  3. 后续访问与缓存命中
    当再次访问计算属性.value,而依赖未变化时,直接返回缓存值,避免重复计算。

    • 检查dirtyfalse
    • 返回value中的缓存结果,无需重新运行
  4. 依赖变化与缓存失效
    当依赖(如count.value)发生变化时:

    • countsetter触发依赖更新(trigger),通知所以依赖它的effect
    • 计算属性内部effect收到更新通知
    • 检查dirtytrue,表示缓存已失效
  5. 下次访问与重新计算
    下次访问计算属性时

    • 检测dirtytrue
    • 重新运行getter函数,计算新值
    • 更新value并重置dirtyfalse
    • 返回新值

在遇到高耗能的计算属性时,有缓存可以避免重复的getter函数执行,从而提高性能。

可写计算属性

默认计算属性是只读的,但可以通过gettersetter函数来创建可写的计算属性。

const count = ref(0)
const computedCount = computed({
  get: () => count.value,
  set: (val) => count.value = val
})

于是可以使用computedCount.value来读取值,也可以使用computedCount.value = newVal来设置值。

上一个值

计算属性可以访问到上一个值,通过访问计算属性的getter的第一个参数来获取计算属性返回的上一个值:

<script setup>
import { ref, computed } from 'vue'

const count = ref(2)

// 这个计算属性在 count 的值小于或等于 3 时,将返回 count 的值。
// 当 count 的值大于等于 4 时,将会返回满足我们条件的最后一个值
// 直到 count 的值再次小于或等于 3 为止。
const alwaysSmall = computed((previous) => {
  if (count.value <= 3) {
    return count.value
  }

  return previous
})
</script>

伪代码实现

function computed(getter) {
    let value //缓存的计算结果
    let dirty = true //是否需要重新计算
    const effect = new ReactiveEffect(getter)

    const computedRef = {
        get value() {
            if (dirty) {
                value = effect.run() //运行getter函数,获取计算结果
                dirty = false
            }
            return value
        }
    };

    effect.onTrigger = () => {
        dirty = true;  
    };

    return computedRef;
}
Theme Jasmine by Kent Liao