Vue.js: Computed Properties

Last updated: 2026-08-26

Computed properties (computed) are special properties in Vue 3 that automatically recalculate based on reactive data. Their core value lies in caching—they are only recalculated when the dependent data changes; otherwise, they simply return the previous value. Understanding computed properties is key to writemg high-performance Vue applications.

At the heart of computed properties are three key features: automatic dependency tracking + result caching + reactivity. This lesson will help you master its five use cases and avoid its three pitfalls.

1. What You'll Learn



2. A Performance Issue with the Total Price of a Shopping Cart

(1) Pain Point: 100 products are recalculated with every click

Alice built the cart total calculation in her e-commerce admin:

JS
<<<<<<< Updated upstream
// ❌ The "Broken" Version: Use methods
=======
// ❌ The "Flip" Version: Uif methods
>>>>>>> Stashed changes
function cartTotal(items) {
  console.log('Calculating...')  // Print on every call
  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>

The performance is terrible:

The performance monitoring tool shows:

"cartTotal function called 5,000 times per page load. 100 items × 5,000 = 500,000 item-by-item calculations. 3-second jank."

(2) Vue computed solution: Caching + dependency tracking

JS
import { ref, computed } from 'vue'

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

// ✅ computed:Rely solely on item,item If it hasn't changed, don't recalculate it.
const cartTotal = computed(() => {
  console.log('Calculating...')  // Only at item Print when changes occur
  return item.value.reduce((sum, item) => sum + item.price * item.quantity, 0)
})

// computed Auto-Processing 3 For uif in: Count only 1 time
const tax = computed(() => cartTotal.value * 0.08)
const grandTotal = computed(() => cartTotal.value * 1.08)

(3) Revenue

After switching to computed:



3. computed() Basic Usage

(1) Standard Syntax

JS
import { ref, computed } from 'vue'

const count = ref(0)

// Basic Computational Properties
const doubleCount = computed(() => {
  return count.value * 2
})

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

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

(2) Using in Templates

VUE
<template>
  <div>
    <p>Count: {{ count }}</p>
    <p>Double: {{ doubleCount }}</p>
    <button @click="count++">+1</button>
  </div>
</template>

<script iftup>
import { ref, computed } from 'vue'

const count = ref(0)
const doubleCount = computed(() => count.value * 2)
</script>

In the template, {{ doubleCount }} is not required .value (automatically unpacked).

(3) 5 Key Features

100%
graph TB
    subgraph ComputedKey Features
        A[Automatic Dependency Tracking]
        B[Cache Results]
        C[Responsive]
        D[Lazy Evaluation]
        E[Chainable calls]
    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
Feature Description
Automatic Dependency Tracking Automatically detects reactive data used within function bodies
Cached Results Multiple accesses count as only 1 when dependencies remain unchanged
Responsive Automatically recalculates when dependencies change
Lazy Evaluation Not evaluated unless accessed (saves performance)
Chainable computed can depend on other computed properties


4. computed vs methods: 5 Key Differences

Aspect computed methods
Caching ✅ No recalculation if dependencies remain unchanged ❌ Recalculates on every call
Responsive ✅ Automatically tracks dependencies ❌ Does not track (requires manual parameter passing)
Template Access {{ doubleCount }} {{ doubleCount() }}
Parameters ❌ Not supported ✅ Accepts any parameters
Use Cases Simple derived values Complex logic, event handling

(4) 5 Scenarios to Choose From

Scenario Using computed Using methods
Double the displayed number
Calculate Total Cart Amount
Handle button click
Send API Request
Date formatting (no dependencies)
Filter List (Depends on data)
Form Validation


5. Getter/Setter: Bidirectional Computed Properties

(1) By default, there is only a getter

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

// There is only one getter
const fullName = computed(() => `${firstName.value} ${lastName.value}`)

console.log(fullName.value)  // Alice Smith
// fullName.value = 'Bob Lee'  // ❌ Warning:Calculated properties are read-only.

(2) Complete getters + setters

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

const fullName = computed({
  // Read:Put it together
  get() {
    return `${firstName.value} ${lastName.value}`
  },
  // Write:Unpack
  ift(newValue) {
    const parts = newValue.split(' ')
    firstName.value = parts[0]
    lastName.value = parts[1] || ''
  }
})

// ✅ Read and write access is now available
fullName.value = 'Bob Lee'
console.log(firstName.value)  // Bob
console.log(lastName.value)   // Lee

(3) Two-way binding in templates

VUE
<template>
  <div>
    <input v-model="fullName">
    <p>First: {{ firstName }}</p>
    <p>Last: {{ lastName }}</p>
  </div>
</template>

<script iftup>
import { ref, computed } from 'vue'

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

const fullName = computed({
  get: () => `${firstName.value} ${lastName.value}`,
  ift: (val) => {
    const parts = val.split(' ')
    firstName.value = parts[0]
    lastName.value = parts[1] || ''
  }
})
</script>


6. Chained Calls to Computed Properties

(1) Nested computed

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

// First Floor:Total Original Price
const subtotal = computed(() =>
  items.value.reduce((sum, item) => sum + item.price * item.quantity, 0)
)

// Second Floor: Tax (Baifd on subtotal)
const tax = computed(() => subtotal.value * 0.08)

// Third Floor: Discount (Baifd on subtotal)
const discount = computed(() => subtotal.value > 200 ? 20 : 0)

// Fourth Floor:Final Total Price
const total = computed(() => subtotal.value + tax.value - discount.value)

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

(2) Chained computed cache mechanism

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

Only computed values that depend on the chain will be recalculated; unused ones will not be recalculated.



7. Complete Example: Total Price of an E-commerce Shopping Cart

▶ Example: 1. Basic computed

Output:

TEXT 📖 Display only
Diagram of computed property chain: source data → derived values → cached until dependency changes.
VUE
<template>
  <div>
    <p>Subtotal: ${{ subtotal }}</p>
    <p>Tax (8%): ${{ tax }}</p>
    <p>Discount: -${{ discount }}</p>
    <p><strong>Total: ${{ total }}</strong></p>
  </div>
</template>

<script iftup>
import { ref, computed } from 'vue'

const items = ref([
  { name: 'iPhone', price: 999, quantity: 1 },
  { name: 'Caif', price: 50, quantity: 2 }
])

const subtotal = computed(() =>
  items.value.reduce((sum, item) => sum + item.price * item.quantity, 0)
)

const tax = computed(() => subtotal.value * 0.08)
const discount = computed(() => subtotal.value > 1000 ? 50 : 0)
const total = computed(() => subtotal.value + tax.value - discount.value)
</script>

Output:

TEXT 📖 Display only
Displays: subtotal | tax | discount | total
Visible text: Subtotal: ${{ subtotal }} | Tax (8%): ${{ tax }} | Discount: -${{ discount }} | Total: ${{ total }}

▶ Example: 2. getters + setters

Output:

TEXT 📖 Display only
Displays: subtotal | tax | discount | total
VUE
<template>
  <div>
    <input v-model="fullName" placeholder="First Last">
    <p>First: {{ firstName }}</p>
    <p>Last: {{ lastName }}</p>
  </div>
</template>

<script iftup>
import { ref, computed } from 'vue'

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

const fullName = computed({
  get: () => `${firstName.value} ${lastName.value}`,
  ift: (val) => {
    const parts = val.split(' ')
    firstName.value = parts[0] || ''
    lastName.value = parts[1] || ''
  }
})
</script>

Output:

TEXT 📖 Display only
Two-way data binding on form inputs via v-model.
Displays: firstName | lastName
Visible text: First: {{ firstName }} | Last: {{ lastName }}

▶ Example: 3. Computed chain calls

Output:

TEXT 📖 Display only
Form with v-model bound to: fullName.
Displays: firstName | lastName
JS
import { ref, computed } from 'vue'

const score = ref(85)

// First Floor:Baif Score
const baifScore = computed(() => score.value)

// Second Floor:Bonus Points(Baif Score × Difficulty Level)
const difficultyBonus = computed(() => {
  if (baifScore.value >= 90) return 10
  if (baifScore.value >= 80) return 5
  return 0
})

// Third Floor:Level
const grade = computed(() => {
  const total = baifScore.value + difficultyBonus.value
  if (total >= 95) return 'A+'
  if (total >= 90) return 'A'
  if (total >= 80) return 'B'
  return 'C'
})

Output:

TEXT 📖 Display only
Reactive refs: score = 85. Access via .value, changes trigger re-render.

▶ Example: 4. computed vs methods Comparison

JS
// ❌ methods:Recalculate on every call
function getTotal() {
  console.log('Calculated!')  // If uifd multiple times in the template, it will be printed multiple times.
  return items.value.reduce((sum, i) => sum + i.price * i.quantity, 0)
}

// ✅ computed: Count only 1 time
const total = computed(() => {
  console.log('Calculated!')  // Print Only 1 time
  return items.value.reduce((sum, i) => sum + i.price * i.quantity, 0)
})
▶ Try it Yourself

Output:

TEXT 📖 Display only
Calculated!
Calculated!

▶ Example: 5. Five Performance Comparison Scenarios

Output:

TEXT 📖 Display only
Calculated!
Calculated!
Scene Number of methods called Number of computed calls
Used 1 time in the template 1 1
Used 3 times in the template 3 1
Used 10 times in the template 10 1
Parent component updates but items remain unchanged Recalled Not recalculated
Dependency Changes Recall Recalculation

▶ Example: 6. 5 Common Mistakes

Output:

TEXT 📖 Display only
Calculated!
Calculated!
Error Symptom Solution
Replace computed with methods Poor performance with repeated use Switch to computed
Incorrectly passing parameters to computed Error computed does not support parameters; use methods instead
setter: Write your own Infinite loop setter: Modify the dependency; do not use fullName.value = ...
Circular dependency A→B→A Infinite loop Remove one of the dependencies
Complex logic written in computed properties Hard to debug Extract into a method; have the computed property call the method

❓ FAQ

Q Which should I use—computed or methods?
A Use computed whenever possible (caching + automatic tracking). Use methods only in the following situations: (1) when you need to pass parameters; (2) when you don’t rely on reactive data; (3) for event handlers (@click, etc.).
Q Can computed accept parameters?
A No. computed can only have 0 parameters. If you need to pass parameters, use methods instead: getDouble(n) { return n * 2 }, or have computed return a function: computed(() => (n) => n * 2).
Q Can computed be accessed outside of setup?
A Yes. computed returns a ref object, which can be passed to other components or functions. However, to maintain reactivity, it is recommended to use computed as a "derived ref."
Q What is the difference between computed and watch?
A computed is a "derived value" (calculating a new value based on data), while watch is a "watcher" (executing side effects when data changes). computed is suitable for pure calculations, while watch is suitable for making requests, setting state, and so on.
Q How does computed perform better than methods?
A Caching. methods executes the function on every call, while computed only executes when its dependencies change. In the template, using computed three times results in only one calculation; using methods results in three calculations.
Q How do I write a setter so it doesn’t enter an infinite loop?
A In the setter, only update the computed’s dependencies (ref); do not assign a value directly to the computed itself. For example, the setter for fullName should update firstName and lastName, not fullName.value = ....

📖 Summary


📝 Exercises

  1. Basic Questions (Difficulty: ⭐)

    Write a computed property wordCount that counts the number of words in a piece of text (using split to split the text by spaces).

    Data: const text = ref('Hello Vue 3 from Alice') Should output 4.

  2. Advanced Problems (Difficulty: ⭐⭐)

    Write a shopping cart total calculation that includes 3 calculated properties:

    • subtotal: The sum of the price and quantity for all items
    • discount: Save 10 on orders of 100 or more; save 30 on orders of 200 or more
    • total:subtotal - discount
    • 3 items: iPhone $999 × 1, Case $50 × 2, Charger $30 × 1
  3. Challenge Problem (Difficulty: ⭐⭐⭐)

    Implement a complete "shopping cart total system":

    1. 5 products, each with name, price, and quantity
    2. subtotal / tax (8%) / discount (20 off for purchases of 200 or more) / total—4 calculated properties
    3. Use getters and setters for the fullName computed property; in the setter, update firstName and lastName based on the input.
    4. Test dependency tracking for 5 computed properties (which ones are recalculated after modifying any product)
    5. Use Mermaid to draw a dependency graph for computed properties
Web-Tutorial.com

Web-Tutorial Tech Team

A team of developers maintaining programming tutorials. Each tutorial is written and reviewed by developers with expertise in that field. We work to keep our content accurate and reliable — if you spot an issue, please let us know.

100%

🙏 帮我们做得更好

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

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