计算属性
用来描述依赖响应式状态的复杂逻辑,
概念
“懒计算”机制,只有在被访问时才会重新计算。接受一个getter函数并返回一个只读响应式ComputedRef
对象。computed()
默认假设传入的就是getter函数。
和方法相比
计算属性值有缓存和懒计算机制:
- 只有访问计算属性的值时才,getter函数才会首次执行
- 计算结果会被缓存,只有当依赖(如
count.value
)发生变化时,缓存才会失效 - 依赖变化后,下次访问时getter会重新运行并更新缓存
缓存的运行原理
初始化与缓存结构:
在定义一个计算属性时,computed()
会创建一个ComputedRef
对象,在其内部维护两个关键状态:value
:存储计算结果,初始未定义,计算尚未发生dirty
:布尔值,表示缓存是否有效(true
表示缓存有效,false
表示缓存无效),初始值为true
。
首次计算:
第一次访问.value
时:- 检查
dirty
是否为true
, 执行
getter
函数- 访问依赖的响应式状态(如
count.value
),触发依赖收集(track
),将响应式状态作为依赖 - 计算结果存储在
value
中
- 访问依赖的响应式状态(如
- 将
dirty
设置为false
,表示缓存已更新
- 检查
后续访问与缓存命中
当再次访问计算属性.value
,而依赖未变化时,直接返回缓存值,避免重复计算。- 检查
dirty
为false
- 返回
value
中的缓存结果,无需重新运行
- 检查
依赖变化与缓存失效
当依赖(如count.value
)发生变化时:count
的setter
触发依赖更新(trigger
),通知所以依赖它的effect
- 计算属性内部effect收到更新通知
- 检查
dirty
为true
,表示缓存已失效
下次访问与重新计算
下次访问计算属性时- 检测
dirty
为true
- 重新运行
getter
函数,计算新值 - 更新
value
并重置dirty
为false
- 返回新值
- 检测
在遇到高耗能的计算属性时,有缓存可以避免重复的getter函数执行,从而提高性能。
可写计算属性
默认计算属性是只读的,但可以通过getter
和setter
函数来创建可写的计算属性。
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;
}