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