深入解析 Vue 3 中的 computed
以及相关知识点
一、引言
在 Vue.js 中,computed
属性用于定义计算属性,是一个基于响应式依赖的缓存值,只有当依赖的数据变化时才会重新计算。它是构建高效、性能优异的 Vue 应用的重要工具。
Vue 3 通过 Composition API 为 computed
提供了更灵活的用法和更强的可扩展性。本文将深入讲解 Vue 3 中 computed
的用法及其相关知识点,并提供基于 语法糖 的完整示例,帮助你快速掌握。
二、computed
的基础用法
在 Vue 3 中,computed
是通过 @vue/reactivity
提供的一个函数,用于创建响应式的计算属性。
1. 定义只读计算属性
示例代码:
<script setup>
import { ref, computed } from 'vue'
// 定义响应式数据
const firstName = ref('张')
const lastName = ref('三')
// 定义计算属性
const fullName = computed(() => `${firstName.value} ${lastName.value}`)
</script>
<template>
<div>
<p>名字:{{ fullName }}</p>
<input v-model="firstName" placeholder="姓氏" />
<input v-model="lastName" placeholder="名字" />
</div>
</template>
输出说明:
fullName
是一个基于firstName
和lastName
计算得出的值。- 当
firstName
或lastName
更新时,fullName
会自动重新计算并更新到视图中。
2. 定义可写计算属性
在某些情况下,我们需要允许计算属性被写入。可以通过 get
和 set
配合实现。
示例代码:
<script setup>
import { ref, computed } from 'vue'
const firstName = ref('张')
const lastName = ref('三')
// 定义可读写的计算属性
const fullName = computed({
get: () => `${firstName.value} ${lastName.value}`,
set: (value) => {
const [first, last] = value.split(' ')
firstName.value = first || ''
lastName.value = last || ''
}
})
</script>
<template>
<div>
<p>名字:{{ fullName }}</p>
<input v-model="fullName" placeholder="全名" />
</div>
</template>
输出说明:
fullName
支持双向绑定:可以从输入框中更新firstName
和lastName
的值。
三、computed
与 watch
的对比
1. 功能上的区别
特性 | computed | watch |
---|---|---|
用途 | 依赖值变化时,自动返回计算后的新值 | 监控一个或多个响应式数据的变化,并执行回调函数 |
是否有返回值 | 有,始终返回计算结果 | 无,回调函数不返回值 |
是否缓存 | 是,只有依赖发生变化时才会重新计算 | 否,每次依赖值变化时都会执行回调 |
2. 选择何时使用
- 使用
computed
:当需要基于一个或多个数据源计算新值,并在模板中直接使用时。 - 使用
watch
:当需要执行副作用操作(如调用 API、写日志等)时。
四、computed
的高级用法
1. 依赖多重数据
计算属性可以依赖多个响应式数据,并自动跟踪这些依赖。
示例代码:
<script setup>
import { ref, computed } from 'vue'
const items = ref([
{ name: '苹果', price: 3 },
{ name: '香蕉', price: 2 },
{ name: '橙子', price: 4 }
])
// 计算总价格
const totalPrice = computed(() => items.value.reduce((sum, item) => sum + item.price, 0))
</script>
<template>
<div>
<ul>
<li v-for="item in items" :key="item.name">{{ item.name }}: {{ item.price }} 元</li>
</ul>
<p>总价:{{ totalPrice }} 元</p>
</div>
</template>
说明:
totalPrice
会根据items
的变化自动更新。- 在 Vue 中,你不需要手动声明依赖关系,
computed
会自动追踪依赖。
2. 配合 watchEffect
使用
有时需要在计算属性中依赖外部副作用的场景,可以配合 watchEffect
使用(副作用是指任何会影响外部状态的行为
,比如打印日志,发起网络请求,操作DOM,调用外部API)。
示例代码:
<script setup>
import { ref, computed, watchEffect } from 'vue'
const basePrice = ref(100)
const discount = ref(10)
const finalPrice = computed(() => basePrice.value - discount.value)
// 打印计算过程
watchEffect(() => {
console.log(`基础价格: ${basePrice.value}, 折扣: ${discount.value}, 最终价格: ${finalPrice.value}`)
})
</script>
<template>
<div>
<p>最终价格:{{ finalPrice }}</p>
<input v-model.number="basePrice" type="number" placeholder="基础价格" />
<input v-model.number="discount" type="number" placeholder="折扣" />
</div>
</template>
五、computed
的原理解析
Vue 3 中的 computed
是基于响应式系统实现的,其原理大致如下:
- 依赖收集:在
computed
的getter
函数中访问响应式数据时,会自动注册对这些数据的依赖。 - 懒惰计算:
computed
默认采用 惰性求值 策略,只有当计算属性被访问时,才会重新计算。 - 缓存机制:
computed
会缓存计算结果,只有在依赖值发生变化时,才会重新计算。
核心源码(简化版):
function computed(getter) {
let cachedValue
let dirty = true
const effect = new ReactiveEffect(getter, () => {
dirty = true
})
return {
get value() {
if (dirty) {
cachedValue = effect.run()
dirty = false
}
return cachedValue
}
}
}
六、常见问题与优化
1. 不要在 computed
中执行副作用
错误示例:
const invalid = computed(() => {
console.log('执行副作用') // 不推荐
return someValue
})
优化建议:
- 将副作用逻辑放在
watch
或watchEffect
中。
2. 避免复杂计算
计算属性过于复杂可能导致性能问题。建议将复杂逻辑拆分成多个小的 computed
属性。
七、总结
computed
是 Vue 3 中高效管理依赖关系的重要工具,通过缓存和懒加载提高性能。- 它适合处理基于响应式数据的派生状态,并提供只读和可写两种模式。
- 与
watch
相比,computed
更适合用于模板中的派生值计算,而watch
更适合处理副作用。