Derived values belong in computed properties, not in duplicated state or expensive template expressions.
A computed property derives a value from other reactive state:
const remaining = computed(() => todos.value.filter(t => !t.done).length)computed() returns a read-only ref, so in script you read remaining.value and in the template simply {{ remaining }}.
Two properties make computeds better than calling a function in the template:
- Dependency tracking: Vue records which reactive values the getter read and recomputes only when one of them changes.
- Caching: reading it repeatedly returns the cached value; a method would re-run on every render.
Keep getters pure: no fetching, no mutating other state, no DOM access. Side effects belong in watchers. Also never mutate the computed result (for example sorting it in place); treat it as a snapshot.
For the rare two-way case, pass get and set to make a writable computed, for example a fullName that splits back into first and last names.
Since Vue 3.4 a computed only notifies dependents when its value actually changes, so const isEven = computed(() => n.value % 2 === 0) does not re-render consumers when n goes from 2 to 4.
A common smell is copying derived data into a ref and keeping it in sync with a watcher. If a value can be computed from existing state, make it a computed.
Example
import { ref, computed } from 'vue'
const todos = ref([
{ text: 'Read docs', done: true },
{ text: 'Build app', done: false },
])
const remaining = computed(() => todos.value.filter(t => !t.done).length)
console.log(remaining.value) // 1
todos.value.push({ text: 'Ship', done: false })
console.log(remaining.value) // 2 (recomputed lazily on read)
const first = ref('Ada'), last = ref('Lovelace')
const fullName = computed({
get: () => `${first.value} ${last.value}`,
set: (v) => { [first.value, last.value] = v.split(' ') },
})
fullName.value = 'Grace Hopper'
console.log(first.value) // GraceRemember
computed(() => ...)returns a cached, read-only ref- It recomputes only when a reactive dependency changes
- Keep getters pure; use get/set only when two-way derivation is needed
Watch out for
- Mutating inside a getter, such as
list.value.sort(), which changes the source state.