Vue Methods vs. Computed Properties: The Complete Performance and Debugging Guide
Every Vue developer faces this choice: use a method or a computed property? The wrong pick can kill performance, trigger console errors, or leave your UI stale. Here’s how to choose—and how to debug the most common pitfalls.
Real-World Pitfalls and Their Console Errors
1. Computed Not Updating
Symptoms:
- The value shown in the template never changes.
- No errors thrown—just stale UI.
Console Warning (Vue 3 internal):
[Vue warn]: Computed getter “filteredItems” has no effect.
Broken Example:
<script setup>
import { ref, computed } from 'vue'
const items = ref([{ id:1, active:true }, { id:2,active:false }])
let filterActive = true // NOT reactive!
const filteredItems = computed(() => {
return items.value.filter(i => i.active === filterActive)
})
</script>
<template>
<button @click="filterActive = !filterActive">Toggle</button>
<div v-for="i in filteredItems" :key="i.id">{{ i.id }}</div>
</template>
Fix: Make filterActive
reactive.
const filterActive = ref(true)
2. “Cannot Read Property of Undefined” in Computed
Symptoms:
- Console error:
TypeError: Cannot read property 'name' of undefined
- Happens when props or data are
null
/undefined
on first render.
Broken Example:
<script setup>
const props = defineProps({ user: Object })
const displayName = computed(() => props.user.name.toUpperCase())
</script>
Fix: Guard against missing data.
const displayName = computed(() => {
return props.user && props.user.name
? props.user.name.toUpperCase()
: 'Guest'
})
3. Methods Causing Hidden Re-Renders
Symptoms:
- Console not warning, but performance profiler shows excessive renders.
- Every unrelated data change triggers the method.
Example:
<script setup>
import { ref } from 'vue'
const user = ref({ name: 'Alice' })
const counter = ref(0)
const getNameLength = () => {
console.log('Calculating length')
return user.value.name.length
}
</script>
<template>
<p>{{ getNameLength() }}</p>
<button @click="counter++">Increment {{ counter }}</button>
</template>
Each click logs “Calculating length”, even though user.name
didn’t change.
Solution: Move to computed.
const nameLength = computed(() => user.value.name.length)
4. “Methods vs Computed” TypeError in Template
Symptoms:
- Console error:
TypeError: _ctx.getFullName is not a function
- Occurs when you mistakenly call a method not defined in
setup
.
Broken Example:
<template>
<p>{{ getFullName() }}</p>
</template>
<script setup>
const fullName = computed(() => 'John Doe')
// Oops: no getFullName defined
</script>
Fix: Either define getFullName
or call the computed:
<p>{{ fullName }}</p>
or
const getFullName = () => fullName.value
5. Computed Cache Invalidation Issues
Symptoms:
- Computed doesn’t recalculate when nested properties change.
- No console errors—just stale results.
Broken Example:
const settings = reactive({ theme: { color: 'dark' } })
const color = computed(() => settings.theme.color)
// Later:
settings.theme = { color: 'light' } // computed updates
settings.theme.color = 'blue' // computed ignores this change!
Fix: Use deep reactivity or toRefs
on nested objects:
const theme = toRefs(settings.theme)
const color = computed(() => theme.color.value)
New Example: Handling Error-Prone Calculations
Scenario: You have a list of items whose price can be null
or a number. You need the total, but null prices break the computed.
Broken Computed:
const items = ref([{ price: 10 }, { price: null }])
const total = computed(() => {
return items.value.reduce((sum, i) => sum + i.price, 0)
})
// TypeError: Cannot read property 'price' of null
Robust Fix:
const total = computed(() =>
items.value.reduce((sum, i) => {
const price = Number(i.price) || 0
return sum + price
}, 0)
)
And if you accidentally destructure items
, you lose reactivity:
const { value: localItems } = items // BAD
Always access items.value
directly or use a computed wrapper.
The Rules at a Glance
- Computed properties
- Cache results until dependencies change
- Only track dependencies accessed during execution
- Guard against
undefined
inputs
- Methods
- Run on every render
- Accept parameters
- Perform side effects
Master these patterns, handle the console errors, and your Vue components will be bulletproof, high-performance, and maintainable.