好的!计算属性(Computed Properties) 是 Vue 中用于处理复杂逻辑并返回派生数据的核心特性,它的设计目标是简化模板中的复杂表达式并自动缓存结果,避免重复计算。以下是详细解释和示例:
核心特点
-
依赖追踪
计算属性会自动追踪其依赖的响应式数据,当依赖发生变化时,计算属性会重新计算;如果依赖未变化,则直接返回缓存值。 -
缓存机制
只有当依赖变化时才会重新计算,避免重复执行复杂逻辑(与普通方法不同,方法在每次渲染时都会执行)。 -
声明式语法
通过computed
函数定义,逻辑清晰,与模板解耦。
基础用法
示例 1:简单计算
<script setup>
import { ref, computed } from 'vue';
const firstName = ref('John');
const lastName = ref('Doe');
// 计算属性:自动拼接全名
const fullName = computed(() => {
return `${firstName.value} ${lastName.value}`;
});
</script>
<template>
<input v-model="firstName" placeholder="First Name">
<input v-model="lastName" placeholder="Last Name">
<p>Full Name: {{ fullName }}</p>
</template>
- 说明:当
firstName
或lastName
变化时,fullName
自动更新,否则使用缓存值。
示例 2:复杂逻辑(过滤列表)
<script setup>
import { ref, computed } from 'vue';
const searchText = ref('');
const todos = ref([
{ id: 1, text: 'Learn Vue', done: true },
{ id: 2, text: 'Build a project', done: false },
]);
// 计算属性:过滤未完成的任务
const filteredTodos = computed(() => {
return todos.value.filter(todo =>
!todo.done && todo.text.includes(searchText.value)
);
});
</script>
<template>
<input v-model="searchText" placeholder="Search...">
<ul>
<li v-for="todo in filteredTodos" :key="todo.id">
{{ todo.text }}
</li>
</ul>
</template>
- 说明:
filteredTodos
依赖todos
和searchText
,任何变化都会触发重新过滤。
计算属性 vs 方法
方法实现(对比)
<script setup>
// 方法:每次渲染都会执行
function getFullName() {
return `${firstName.value} ${lastName.value}`;
}
</script>
<template>
<p>{{ getFullName() }}</p> <!-- 每次渲染都调用 -->
<p>{{ fullName }}</p> <!-- 依赖变化时才计算 -->
</template>
- 关键区别:方法无缓存,计算属性有缓存。
计算属性的 Setter
计算属性默认是只读的,但可以通过 get
/set
定义可写属性:
<script setup>
import { ref, computed } from 'vue';
const firstName = ref('John');
const lastName = ref('Doe');
const fullName = computed({
get() {
return `${firstName.value} ${lastName.value}`;
},
set(newValue) {
// 当修改 fullName 时,反向拆分赋值
const [first, last] = newValue.split(' ');
firstName.value = first;
lastName.value = last || '';
}
});
// 测试:修改 fullName
setTimeout(() => {
fullName.value = 'Jane Smith'; // 触发 set 方法
}, 2000);
</script>
- 应用场景:需要双向绑定的派生数据(较少使用)。
注意事项
-
避免副作用
计算属性应专注于计算,不要在其中执行异步操作或修改 DOM。 -
依赖必须响应式
如果依赖非响应式数据(如普通变量),计算属性不会更新。 -
异步操作不适用
计算属性不能处理异步逻辑,需改用watch
或方法。
总结:何时使用计算属性?
- 派生数据
需要基于现有数据生成新数据(如过滤、排序、格式化)。 - 复杂逻辑
模板中不宜写复杂表达式时,用计算属性解耦。 - 性能优化
依赖变化时才重新计算,避免重复执行高开销操作。
问题 1:为什么 fullName
用 const
而不是 var
?
原因:
const
的作用:const
声明的变量不允许重新赋值(即不能指向新值),但不限制其内部值的变更(如果变量是对象或数组)。computed
的特性:fullName
是computed
返回的计算属性引用,其值由内部的getter
函数动态生成,不需要重新赋值。- 防止意外覆盖:用
const
可以避免开发中误操作对fullName
重新赋值(例如fullName = newValue
),保持代码安全。
const fullName = computed(() => `${firstName.value} ${lastName.value}`);
// ❌ 错误!const 禁止重新赋值
fullName = computed(() => '其他值');
// ✅ 允许修改依赖的响应式数据(firstName/lastName)
firstName.value = 'Alice';
问题 2:fullName
没有被 ref
包裹,为何视图会更新?
原因:
computed
的返回值是响应式的:computed
内部会创建一个响应式引用(类似ref
生成的引用对象),自动追踪依赖(firstName
和lastName
)。- 依赖的响应式性:
firstName
和lastName
是ref
创建的响应式数据,当它们变化时,computed
会重新计算fullName
,并触发视图更新。
验证示例:
<script setup>
import { ref, computed } from 'vue';
const firstName = ref('John');
const lastName = ref('Doe');
// fullName 是 computed 生成的响应式引用
const fullName = computed(() => `${firstName.value} ${lastName.value}`);
function changeName() {
firstName.value = 'Alice'; // 修改依赖项 → 触发 fullName 重新计算
}
</script>
<template>
<p>{{ fullName }}</p> <!-- 视图会更新! -->
<button @click="changeName">修改名字</button>
</template>
总结
const
的作用:防止fullName
被意外重新赋值,与响应式无关。computed
的响应式性:computed
返回的引用本身是响应式的,且自动追踪其依赖的ref
/reactive
数据的变化,无需手动包裹ref
。