Vue.js: 计算属性 computed

最后更新:2026-10-20

计算属性(computed)是 Vue 3 中基于响应式数据自动重算的特殊属性。它的核心价值是缓存——只有依赖的数据变化时才重新计算,否则直接返回上次的值。理解 computed 是写出高性能 Vue 应用的关键。

计算属性的核心是 3 个特性:自动追踪依赖 + 缓存结果 + 响应式。本课帮你掌握它的 5 种用法和 3 个陷阱。

相关核心:computed()

1. 你将学到


2. 一个购物车总价的性能问题

(1) 痛点:每次点击都重新算 100 个商品

Alice 在电商后台实现了购物车总价计算:

JS
// ❌ 翻车版:用 methods
function cartTotal(items) {
  console.log('Calculating...')  // 每次调用都打印
  return items.reduce((sum, item) => sum + item.price * item.quantity, 0)
}
HTML
<template>
  <div>
    <p>Total: ${{ cartTotal(items) }}</p>
    <p>Tax: ${{ cartTotal(items) * 0.08 }}</p>
    <p>Grand Total: ${{ cartTotal(items) * 1.08 }}</p>
    <button @click="showDate = new Date()">Update Time</button>
  </div>
</template>

性能极差:

性能监控工具显示:

"cartTotal 函数每次页面加载调用 5,000 次。100 个商品 × 5,000 = 500,000 次逐项计算。3 秒卡顿。"

(2) Vue computed 解法:缓存 + 依赖追踪

JS
import { ref, computed } from 'vue'

const items = ref([
  { price: 100, quantity: 2 },
  { price: 50, quantity: 1 }
])

// ✅ computed:只依赖 items,items 不变就不重算
const cartTotal = computed(() => {
  console.log('Calculating...')  // 只在 items 变化时打印
  return items.value.reduce((sum, item) => sum + item.price * item.quantity, 0)
})

// computed 自动处理 3 处使用:只算 1 次
const tax = computed(() => cartTotal.value * 0.08)
const grandTotal = computed(() => cartTotal.value * 1.08)

(3) 收益

切换到 computed 后:


3. computed() 基本用法

(1) 标准语法

JS
import { ref, computed } from 'vue'

const count = ref(0)

// 基础计算属性
const doubleCount = computed(() => {
  return count.value * 2
})

// 简写
const doubleCount = computed(() => count.value * 2)

// 使用
console.log(doubleCount.value)  // 0
count.value = 5
console.log(doubleCount.value)  // 10

(2) 模板中使用

模板中 {{ doubleCount }} 不需要 .value(自动解包)。

(3) 5 大特性

100%
graph TB
    subgraph Computed核心特性
        A[自动追踪依赖]
        B[缓存结果]
        C[响应式]
        D[懒计算]
        E[可链式调用]
    end
    
    style A fill:#42b883,color:#fff
    style B fill:#42b883,color:#fff
    style C fill:#42b883,color:#fff
    style D fill:#42b883,color:#fff
    style E fill:#42b883,color:#fff
特性 说明
自动追踪依赖 自动检测函数体中用到的响应式数据
缓存结果 依赖不变时多次访问只算 1 次
响应式 依赖变化时自动重新计算
懒计算 不被访问就不算(节省性能)
可链式 computed 可以依赖其他 computed

▶ 示例:基础 computed

▶ 试一试

输出:

TEXT
Subtotal: $1099
Tax (8%): $87.92
Discount: -$50
Total: $1136.92
点"+ 加 1 件 Case" → Subtotal $1149、Tax $91.92、Total $1190.92

4. computed vs methods 5 大区别

维度 computed methods
缓存 ✅ 依赖不变就不重算 ❌ 每次调用都重算
响应式 ✅ 自动追踪依赖 ❌ 不追踪(要手动传参)
模板访问 {{ doubleCount }} {{ doubleCount() }}
参数 ❌ 不支持 ✅ 接受任意参数
适用场景 简单派生值 复杂逻辑、事件处理

(4) 5 个选择场景

场景 用 computed 用 methods
显示数字翻倍 ✅ ❌
计算购物车总价 ✅ ❌
处理按钮点击 ❌ ✅
发送 API 请求 ❌ ✅
格式化日期(无依赖) ❌ ✅
过滤列表(依赖 data) ✅ ❌
表单校验 ❌ ✅

▶ 示例:computed vs methods 对比

▶ 试一试

输出:

TEXT
Computed (1 次): 200 | 200 | 200
Methods (3 次): 200 | 200 | 200
控制台:computed called 只打印 1 次,methods called 打印 3 次

(1) 📊 5 个性能对比场景

场景 methods 调用次数 computed 调用次数
模板中 1 处使用 1 1
模板中 3 处使用 3 1
模板中 10 处使用 10 1
父组件更新但 items 不变 重新调用 不重算
依赖变化 重新调用 重新计算

(2) 📊 5 个常见错误

错误 现象 解决
用 methods 替代 computed 多次使用性能差 改用 computed
误给 computed 传参 报错 computed 不支持参数,用 methods
setter 写自己 无限循环 setter 改依赖,不要 fullName.value = ...
循环依赖 A→B→A 死循环 拆掉其中一个依赖
复杂逻辑写在 computed 难调试 拆出方法,computed 调方法

5. getter/setter 双向计算属性

(1) 默认只有 getter

JS
const firstName = ref('Alice')
const lastName = ref('Smith')

// 只有一个 getter
const fullName = computed(() => `${firstName.value} ${lastName.value}`)

console.log(fullName.value)  // Alice Smith
// fullName.value = 'Bob Lee'  // ❌ 警告:计算属性是只读的

(2) 完整 getter + setter

JS
const firstName = ref('Alice')
const lastName = ref('Smith')

const fullName = computed({
  // 读取:拼起来
  get() {
    return `${firstName.value} ${lastName.value}`
  },
  // 写入:拆开
  set(newValue) {
    const parts = newValue.split(' ')
    firstName.value = parts[0]
    lastName.value = parts[1] || ''
  }
})

// ✅ 现在可读可写
fullName.value = 'Bob Lee'
console.log(firstName.value)  // Bob
console.log(lastName.value)   // Lee

(3) 模板中双向绑定

▶ 示例:getter + setter

▶ 试一试

输出:

TEXT
输入框初值:Alice Smith
First: Alice
Last: Smith
输入框改成 "Bob Jones" → First: Bob、Last: Jones(setter 反向拆分)

6. 计算属性链式调用

(1) 嵌套 computed

JS
const items = ref([
  { price: 100, quantity: 2 },
  { price: 50, quantity: 3 }
])

// 第一层:原价合计
const subtotal = computed(() =>
  items.value.reduce((sum, item) => sum + item.price * item.quantity, 0)
)

// 第二层:税(基于 subtotal)
const tax = computed(() => subtotal.value * 0.08)

// 第三层:折扣(基于 subtotal)
const discount = computed(() => subtotal.value > 200 ? 20 : 0)

// 第四层:最终总价
const total = computed(() => subtotal.value + tax.value - discount.value)

console.log(total.value)  // 350 * 1.08 - 20 = 358

(2) 链式 computed 缓存机制

100%
graph TB
    A[items change] --> B[subtotal 重新计算]
    B --> C[tax 重新计算]
    B --> D[discount 重新计算]
    C --> E[total 重新计算]
    D --> E
    
    style A fill:#42b883
    style B fill:#42b883
    style E fill:#42b883

只有依赖链上的 computed 会重算,未被使用的不会算。


7. 综合演练:电商购物车总价

▶ 示例:computed 链式调用

▶ 试一试

输出:

TEXT
基础分: 85
附加分: 5
总分: 90
等级: B
点"+5 分" → 基础分 90、附加分 10、总分 100、等级 A+

相关核心:computed()

❓ 常见问题

Q computed 和 methods 到底用哪个?
A 能用 computed 就用 computed(缓存 + 自动追踪)。只有以下情况用 methods:(1) 需要传参;(2) 不依赖响应式数据;(3) 事件处理器(@click 等)。
Q computed 能传参吗?
A 不能。computed 只能有 0 个参数。如果需要传参,用 methods 替代:getDouble(n) { return n * 2 },或用 computed 返回一个函数:computed(() => (n) => n * 2)。
Q computed 在 setup 外能访问吗?
A 能。computed 返回的是 ref 对象,可以传给其他组件或函数。但要保持响应式,建议把 computed 当作"派生 ref"用。
Q computed 和 watch 有什么区别?
A computed 是"派生值"(基于数据计算新值),watch 是"监听器"(数据变化时执行副作用)。computed 适合纯计算,watch 适合发请求、设状态等。
Q computed 性能比 methods 好在哪?
A 缓存。methods 每次调用都执行函数,computed 只在依赖变化时执行。模板中 3 处用 computed,只算 1 次;用 methods 算 3 次。
Q setter 怎么写才不无限循环?
A setter 中只改 computed 的依赖(ref),不要赋值给 computed 自己。例如 fullName 的 setter 应该改 firstName / lastName,不是 fullName.value = ...。
Q computed 可以异步吗?
A 不能。computed 是同步计算,异步用 watch 或 watchEffect。如果需要"异步计算属性",用 data() { return { result: null } } + watch + onMounted 组合。

📖 小节


📝 作业

  1. 基础题(难度⭐) 写一个计算属性 wordCount,统计一段文本的单词数(用 split 拆分空格)。

    数据:const text = ref('Hello Vue 3 from Alice') 应输出 4。

  2. 进阶题(难度⭐⭐) 写一个购物车总价计算,包含 3 个计算属性:

    • subtotal:所有商品 price × quantity 之和
    • discount:满 100 减 10,满 200 减 30
    • total:subtotal - discount
    • 3 个商品:iPhone $999 × 1, Case $50 × 2, Charger $30 × 1
  3. 挑战题(难度⭐⭐⭐) 实现一个完整的"购物车总价系统":

    1. 5 个商品,每个含 name/price/quantity
    2. subtotal / tax(8%) / discount(满 200 减 20) / total 4 个计算属性
    3. fullName 计算属性用 getter/setter,setter 中根据输入更新 firstName/lastName
    4. 测试 5 个计算属性的依赖追踪(修改任意商品后哪些会重算)
    5. 用 mermaid 画出计算属性依赖图
Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏