Appearance
✨ 计算属性 👌
背景:模板表达式的局限
模板表达式用于对原始数据做简单的二次处理:
vue
<template>
<span>Name: {{ author.name }}</span>
<p>Has published books:</p>
<span>{{ author.books.length > 0 ? "Yes" : "No" }}</span>
<!-- 这里用到了复杂表达式 -->
<!-- 缺点:难复用、模板臃肿、不支持语句级复杂逻辑 -->
</template>
<script setup>
import { reactive } from "vue";
const author = reactive({
name: "John Doe",
books: [
"Vue 2 - Advanced Guide",
"Vue 3 - Basic Guide",
"Vue 4 - The Mystery",
],
});
</script>为什么需要计算属性?
- 将“数据派生逻辑”从模板中剥离出来,提升可读性与复用性。
- 支持复杂逻辑与缓存,减少重复计算。
基本使用:computed(getter)
vue
<template>
<span>Name: {{ author.name }}</span>
<p>Has published books:</p>
<span>{{ isPublishBook }}</span>
</template>
<script setup>
import { reactive, computed } from "vue";
const author = reactive({
name: "John Doe",
books: [
"Vue 2 - Advanced Guide",
"Vue 3 - Basic Guide",
"Vue 4 - The Mystery",
],
});
const isPublishBook = computed(() => {
// 对响应式数据进行二次计算
return author.books.length > 0 ? "Yes" : "No";
});
// 依赖变化 -> 自动重新计算
setTimeout(() => {
author.books = [];
}, 2000);
</script>重要
- 计算属性会缓存:只有“依赖的响应式数据”变化时才重新计算。
computed返回一个ref,模板中可直接使用,脚本中需.value。
可写计算属性:computed({ get, set })
当需要“派生值 ↔ 源值”的双向映射时,使用可写计算属性:
vue
<template>
<span>name: {{ fullName }}</span>
</template>
<script setup>
import { ref, computed } from "vue";
const firstName = ref("Xie");
const lastName = ref("Jie");
const fullName = computed({
get() {
return firstName.value + " " + lastName.value;
},
set(newName) {
[firstName.value, lastName.value] = newName.split(" ");
},
});
// 写入计算属性 -> 通过 setter 更新源值
setTimeout(() => {
fullName.value = "Aiden Kao";
}, 3000);
</script>使用场景
- 表单展示与存储字段的映射(例如
fullName与first/last)。
注意
可写计算属性仅在确有双向映射需求时使用;多数场景下计算属性应保持只读。
最佳实践与注意事项
Getter 不应有副作用
- Getter 只做“派生计算”,不要修改外部状态,也不要进行 I/O、请求、DOM 操作或
console.log等副作用。
js
// 副作用示例(不推荐):更改外部系统状态
function effect() {
document.body.innerText = "hello";
}
// 更改全局变量也是副作用
let val = 1;
function effect2() {
val = 2;
}避免直接写计算属性
计算属性的值是派生状态(缓存快照)。应更新其“源状态”来触发新的计算,而不是直接修改计算属性本身。
计算属性 vs 方法
二者都能在模板中使用,但行为不同:
vue
<template>
<button @click="a++">A++</button>
<button @click="b++">B++</button>
<p>computedA: {{ computedA }}</p>
<p>computedB: {{ computedB }}</p>
<p>methodA: {{ methodA() }}</p>
<p>methodB: {{ methodB() }}</p>
</template>
<script setup>
import { ref, computed } from "vue";
const a = ref(1);
const b = ref(1);
const computedA = computed(() => {
console.log("计算属性A重新计算了");
return a.value + 1;
});
const computedB = computed(() => {
console.log("计算属性B重新计算了");
return b.value + 1;
});
function methodA() {
console.log("methodA执行了");
return a.value + 1;
}
function methodB() {
console.log("methodB执行了");
return b.value + 1;
}
</script>结论
- 计算属性:基于依赖的缓存;依赖不变则不重新计算,适合派生数据。
- 方法:每次渲染都会执行,适合事件处理或非派生逻辑。
小结
- 用计算属性替代模板中的复杂表达式,提升可读性与复用性。
- 计算属性会缓存并在依赖变化时重算;脚本中通过
.value访问。 - 仅在确有双向映射时使用可写计算属性。
- Getter 必须是纯函数,无副作用;避免直接写计算属性。
- 需要事件处理或非派生逻辑时使用方法,派生值优先使用计算属性。
