Skip to content

✨ 计算属性 👌

背景:模板表达式的局限

模板表达式用于对原始数据做简单的二次处理:

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>

使用场景

  • 表单展示与存储字段的映射(例如 fullNamefirst/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 必须是纯函数,无副作用;避免直接写计算属性。
  • 需要事件处理或非派生逻辑时使用方法,派生值优先使用计算属性。