Computed, methods and watchers in Vue.js
Introduction
Vue has three ways to work with reactive data: computed, methods, and watchers. Each has its purpose and characteristics.
Computed Properties
Computed properties are calculated based on other reactive data and are cached.
Composition API
<template>
<p>First Name: {{ firstName }}</p>
<p>Last Name: {{ lastName }}</p>
<p>Full Name: {{ fullName }}</p>
</template>
<script setup>
import { ref, computed } from 'vue'
const firstName = ref('John')
const lastName = ref('Doe')
const fullName = computed(() => {
return `${firstName.value} ${lastName.value}`
})
</script>Key Features
Caching
Result is calculated only when dependencies change:
<script setup>
import { ref, computed } from 'vue'
const count = ref(0)
const doubleCount = computed(() => {
console.log('Computing...') // Called only when count changes
return count.value * 2
})
</script>
<template>
<p>{{ doubleCount }}</p>
<p>{{ doubleCount }}</p>
<p>{{ doubleCount }}</p>
<!-- console.log called only once -->
</template>Writable Computed
<script setup>
import { ref, computed } from 'vue'
const firstName = ref('John')
const lastName = ref('Doe')
const fullName = computed({
get() {
return `${firstName.value} ${lastName.value}`
},
set(newValue) {
[firstName.value, lastName.value] = newValue.split(' ')
}
})
// Usage
fullName.value = 'Jane Smith'
// firstName.value = 'Jane'
// lastName.value = 'Smith'
</script>Methods
Methods are functions that execute every time they're called.
<template>
<p>{{ getFullName() }}</p>
<button @click="greet">Greet</button>
</template>
<script setup>
import { ref } from 'vue'
const firstName = ref('John')
const lastName = ref('Doe')
function getFullName() {
console.log('Method called')
return `${firstName.value} ${lastName.value}`
}
function greet() {
alert(`Hello, ${getFullName()}!`)
}
</script>Key Features
No Caching
<template>
<p>{{ getFullName() }}</p>
<p>{{ getFullName() }}</p>
<p>{{ getFullName() }}</p>
<!-- console.log called 3 times -->
</template>Accept Arguments
<template>
<p>{{ formatPrice(100) }}</p>
<p>{{ formatPrice(250, 'USD') }}</p>
</template>
<script setup>
function formatPrice(price, currency = 'USD') {
return `${price} ${currency}`
}
</script>Watchers
Watchers are functions that react to data changes and perform side effects.
<template>
<input v-model="searchQuery" placeholder="Search..." />
</template>
<script setup>
import { ref, watch } from 'vue'
const searchQuery = ref('')
watch(searchQuery, (newValue, oldValue) => {
console.log(`Changed from "${oldValue}" to "${newValue}"`)
// Perform API request
fetchResults(newValue)
})
function fetchResults(query) {
// API request
}
</script>Key Features
Watch Multiple Sources
<script setup>
import { ref, watch } from 'vue'
const firstName = ref('')
const lastName = ref('')
watch([firstName, lastName], ([newFirst, newLast]) => {
console.log(`Full name: ${newFirst} ${newLast}`)
})
</script>Immediate Execution
<script setup>
import { ref, watch } from 'vue'
const count = ref(0)
watch(count, (newValue) => {
console.log('Count changed:', newValue)
}, { immediate: true })
// Called immediately on creation
</script>Deep Watching
<script setup>
import { reactive, watch } from 'vue'
const user = reactive({
name: 'John',
address: {
city: 'New York'
}
})
watch(user, (newValue) => {
console.log('User changed:', newValue)
}, { deep: true })
// Triggers even when user.address.city changes
user.address.city = 'Los Angeles'
</script>Comparison
Computed vs Methods
| Property | Computed | Methods |
|---|---|---|
| Caching | Yes | No |
| Reactivity | Automatic | Manual |
| Arguments | No | Yes |
| Usage in template | {{ computed }} | {{ method() }} |
| Side effects | Not recommended | Allowed |
When to use computed:
- Calculate based on other data
- Need caching
- Synchronous operations
When to use methods:
- Event handlers
- Need parameters
- Side effects (API requests, data changes)
Computed vs Watchers
| Property | Computed | Watchers |
|---|---|---|
| Purpose | Calculate value | Side effects |
| Result | Returns value | No return |
| Dependencies | Automatic | Explicit |
| Async | No | Yes |
Common Mistakes
Using methods instead of computed
<!-- Inefficient -->
<template>
<p>{{ getFullName() }}</p>
<p>{{ getFullName() }}</p>
<!-- Called twice -->
</template>
<!-- Efficient -->
<template>
<p>{{ fullName }}</p>
<p>{{ fullName }}</p>
<!-- Calculated once -->
</template>
<script setup>
import { computed } from 'vue'
const fullName = computed(() => {
return `${firstName.value} ${lastName.value}`
})
</script>Side effects in computed
<!-- Wrong -->
<script setup>
import { computed, ref } from 'vue'
const count = ref(0)
const doubled = ref(0)
const bad = computed(() => {
doubled.value = count.value * 2 // Side effect!
return count.value * 2
})
</script>
<!-- Correct - use watcher -->
<script setup>
import { watch, ref } from 'vue'
const count = ref(0)
const doubled = ref(0)
watch(count, (newCount) => {
doubled.value = newCount * 2
})
</script>Conclusion
Computed:
- Calculate values based on other data
- Automatic caching
- Synchronous operations
Methods:
- Event handlers
- Functions with parameters
- Side effects
Watchers:
- React to changes
- Asynchronous operations
- Side effects (API, localStorage)
In interviews:
Important to be able to:
- Explain the difference between computed, methods and watchers
- Describe caching in computed
- Give examples of when to use each
- Explain performance issues with wrong choice
Content
IntroductionComputed PropertiesComposition APIKey FeaturesMethodsKey FeaturesWatchersKey FeaturesComparisonComputed vs MethodsComputed vs WatchersCommon MistakesUsing methods instead of computedSide effects in computedConclusion
Short Answer
Interview readyA concise answer to help you respond confidently on this topic during an interview.