Vue.js: Computed Properties
Last updated: 2026-10-20
Computed properties (computed) are special properties in Vue 3 that recalculate automatically from reactive data. Their core value is caching — they only recompute when a dependency changes; otherwise they return the last value. Understanding computed is the key to writing high-performance Vue apps.
Computed properties rest on three characteristics: automatic dependency tracking + cached results + reactivity. This lesson covers 5 usage patterns and 3 pitfalls.
1. What You'll Learn
- The
computed()syntax and what it returns - The key difference between computed and methods (caching)
- Two-way computed properties with getter/setter
- Chaining computed properties (computed depending on computed)
- How computed behaves in templates versus in methods
- 3 common pitfalls (misuse, circular dependencies, performance problems)
2. A Shopping Cart Total Performance Problem
(1) The Pain: Recalculating 100 Products on Every Click
Alice implemented the cart total in an e-commerce admin:
// ❌ The broken version: using methods
function cartTotal(items) {
console.log('Calculating...') // prints on every call
return items.reduce((sum, item) => sum + item.price * item.quantity, 0)
}
<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>
The performance is terrible:
- Page load: the calculation runs once ✅
- One render: 3 calls (Total, Tax, Grand Total) = 300 product calculations
- The user clicks "Update Time" (unrelated to products): nothing changed, yet the calculation still runs 3 times = 300 product calculations
The performance monitor reports:
"The cartTotal function is called 5,000 times per page load. 100 products × 5,000 = 500,000 per-item calculations. 3 seconds of jank."
(2) The Vue computed Solution: Cache + Dependency Tracking
import { ref, computed } from 'vue'
const items = ref([
{ price: 100, quantity: 2 },
{ price: 50, quantity: 1 }
])
// ✅ computed: depends only on items, so it does not recompute while items is unchanged
const cartTotal = computed(() => {
console.log('Calculating...') // prints only when items changes
return items.value.reduce((sum, item) => sum + item.price * item.quantity, 0)
})
// computed handles all 3 usages automatically: it calculates once
const tax = computed(() => cartTotal.value * 0.08)
const grandTotal = computed(() => cartTotal.value * 1.08)
(3) The Payoff
After switching to computed:
- Function calls: 5,000 → 100 (-98%)
- Time to interactive: 3s → 80ms (-97%)
- Scroll jank: 5 times per second → 0 (-100%)
- CPU usage: 80% → 5% (-94%)
3. computed() Basics
(1) Standard Syntax
import { ref, computed } from 'vue'
const count = ref(0)
// Basic computed property
const doubleCount = computed(() => {
return count.value * 2
})
// Shorthand
const doubleCount = computed(() => count.value * 2)
// Usage
console.log(doubleCount.value) // 0
count.value = 5
console.log(doubleCount.value) // 10
(2) Using It in Templates
Inside the template, {{ doubleCount }} does not need .value (it unwraps automatically).
(3) 5 Key Characteristics
graph TB
subgraph Computed Core Characteristics
A[Automatic dependency tracking]
B[Cached results]
C[Reactivity]
D[Lazy evaluation]
E[Chainable]
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
| Characteristic | Description |
|---|---|
| Automatic dependency tracking | Detects the reactive data used inside the function body |
| Cached results | Repeated access computes once while dependencies are unchanged |
| Reactive | Recomputes automatically when a dependency changes |
| Lazy | Nothing is computed until it is accessed (saves work) |
| Chainable | A computed can depend on other computeds |
▶ Example: Basic computed
▶ Try it YourselfOutput:
Subtotal: $1099, Tax (8%): $87.92, Discount: -$50, Total: $1136.92.
Each click on + Add 1 Case adds $50 to the subtotal, so tax and total recompute automatically.
Subtotal: $1099
Tax (8%): $87.92
Discount: -$50
Total: $1136.92
4. computed vs methods: 5 Key Differences
| Dimension | computed | methods |
|---|---|---|
| Caching | ✅ No recompute while dependencies are unchanged | ❌ Recomputes on every call |
| Reactivity | ✅ Tracks dependencies automatically | ❌ No tracking (pass values in manually) |
| Template access | {{ doubleCount }} |
{{ doubleCount() }} |
| Parameters | ❌ Not supported | ✅ Accepts any arguments |
| Best for | Simple derived values | Complex logic, event handling |
(4) 5 Selection Scenarios
| Scenario | Use computed | Use methods |
|---|---|---|
| Displaying a doubled number | ✅ | ❌ |
| Calculating a cart total | ✅ | ❌ |
| Handling button clicks | ❌ | ✅ |
| Sending API requests | ❌ | ✅ |
| Formatting a date (no dependencies) | ❌ | ✅ |
| Filtering a list (depends on data) | ✅ | ❌ |
| Form validation | ❌ | ✅ |
▶ Example: computed vs methods
▶ Try it YourselfOutput:
Both lines display 200.
The console shows computed called once, but methods called three times — the computed result is cached and reused.
Computed (1 call): 200 | 200 | 200
Methods (3 calls): 200 | 200 | 200
console: computed called
console: methods called
console: methods called
console: methods called
(1) 📊 5 Performance Comparison Scenarios
| Scenario | methods calls | computed calls |
|---|---|---|
| Used once in the template | 1 | 1 |
| Used 3 times in the template | 3 | 1 |
| Used 10 times in the template | 10 | 1 |
| Parent re-renders but items is unchanged | Called again | No recompute |
| A dependency changes | Called again | Recomputes |
(2) 📊 5 Common Mistakes
| Mistake | Symptom | Fix |
|---|---|---|
| Replacing computed with methods | Poor performance when used repeatedly | Switch to computed |
| Passing parameters to computed | Error | computed takes no parameters — use methods |
| Setter writing to itself | Infinite loop | Have the setter update dependencies, never fullName.value = ... |
| Circular dependency A→B→A | Deadlock | Remove one of the dependencies |
| Complex logic inside computed | Hard to debug | Extract methods and call them from computed |
5. getter/setter: Two-Way Computed Properties
(1) Getter Only by Default
const firstName = ref('Alice')
const lastName = ref('Smith')
// Only a getter
const fullName = computed(() => `${firstName.value} ${lastName.value}`)
console.log(fullName.value) // Alice Smith
// fullName.value = 'Bob Lee' // ❌ warning: computed properties are read-only
(2) Full getter + setter
const firstName = ref('Alice')
const lastName = ref('Smith')
const fullName = computed({
// Read: join the parts
get() {
return `${firstName.value} ${lastName.value}`
},
// Write: split them apart
set(newValue) {
const parts = newValue.split(' ')
firstName.value = parts[0]
lastName.value = parts[1] || ''
}
})
// ✅ Now it is readable and writable
fullName.value = 'Bob Lee'
console.log(firstName.value) // Bob
console.log(lastName.value) // Lee
(3) Two-Way Binding in Templates
▶ Example: getter + setter
▶ Try it YourselfOutput:
The input starts with Alice Smith, and the two lines read First: Alice / Last: Smith.
Typing Bob Lee rewrites them to First: Bob / Last: Lee through the setter.
[ Alice Smith ]
First: Alice
Last: Smith
6. Chaining Computed Properties
(1) Nested computed
const items = ref([
{ price: 100, quantity: 2 },
{ price: 50, quantity: 3 }
])
// Layer 1: subtotal of list prices
const subtotal = computed(() =>
items.value.reduce((sum, item) => sum + item.price * item.quantity, 0)
)
// Layer 2: tax (based on subtotal)
const tax = computed(() => subtotal.value * 0.08)
// Layer 3: discount (based on subtotal)
const discount = computed(() => subtotal.value > 200 ? 20 : 0)
// Layer 4: final total
const total = computed(() => subtotal.value + tax.value - discount.value)
console.log(total.value) // 350 * 1.08 - 20 = 358
(2) How Chained computed Caching Works
graph TB
A[items change] --> B[subtotal recomputes]
B --> C[tax recomputes]
B --> D[discount recomputes]
C --> E[total recomputes]
D --> E
style A fill:#42b883
style B fill:#42b883
style E fill:#42b883
Only the computeds on the dependency chain recompute; ones that are never used are skipped.
7. Putting It Together: E-commerce Cart Total
▶ Example: Chaining computed
▶ Try it YourselfOutput:
Base score: 85, Bonus: 5, Total: 90, Grade: A.
Clicking +5 points raises the score to 90, so the bonus becomes 10 and the grade jumps to A+.
Base score: 85
Bonus: 5
Total: 90
Grade: A
❓ FAQ
getDouble(n) { return n * 2 }), or have computed return a function: computed(() => (n) => n * 2).fullName should update firstName / lastName, not fullName.value = ....data() { return { result: null } } with watch + onMounted.📖 Summary
- computed is a "derived ref" that recalculates from reactive data, with 5 characteristics: automatic tracking, caching, reactivity, laziness, and chaining
{{ doubleCount }}in a template needs no.value(automatic unwrapping); in JS usedoubleCount.value- computed vs methods: computed caches (better performance), methods does not cache but accepts parameters
- getter/setter makes a computed two-way: readable and writable, and the setter updates dependencies rather than itself
- Chaining: a computed can depend on other computeds, and unused ones are never recalculated
- 3 pitfalls: circular dependencies, using methods by mistake, and complex logic that is hard to debug
- Performance: three usages of computed in a template calculate once, while methods calculates three times
📝 Exercises
-
Basic (⭐) Write a computed property
wordCountthat counts the words in a piece of text (split on spaces).Data:
const text = ref('Hello Vue 3 from Alice')should output 4. -
Intermediate (⭐⭐) Write a cart total calculation with 3 computed properties:
subtotal: the sum of price × quantity across all itemsdiscount: $10 off from $100, $30 off from $200total: subtotal - discount- 3 items: iPhone $999 × 1, Case $50 × 2, Charger $30 × 1
-
Challenge (⭐⭐⭐) Build a complete "cart total system":
- 5 products, each with name/price/quantity
- 4 computed properties: subtotal / tax (8%) / discount ($20 off from $200) / total
- A
fullNamecomputed using getter/setter, where the setter updates firstName/lastName from the input - Test dependency tracking for all 5 computeds (which ones recompute when any product changes)
- Draw the computed dependency graph with mermaid