Computed properties
A computed property is a value that Vue derives from other state and recalculates when that state changes. For example, the number of unfinished tasks depends on the task list, so there is no need to store it separately.
The computed function
In this lesson tasks are objects with id, text and done fields:
<!-- src/App.vue -->
<script setup>
import { ref, computed } from 'vue'
const todos = ref([
{ id: 1, text: 'Buy bread', done: true },
{ id: 2, text: 'Call Anna', done: false },
{ id: 3, text: 'Write a report', done: false },
])
const remaining = computed(() => {
return todos.value.filter((todo) => !todo.done).length
})
</script>
<template>
<p>Tasks left: {{ remaining }}</p>
</template>The page shows "Tasks left: 2". computed takes a function that returns a value and creates an object similar to a ref: in the template it is just remaining, in the script remaining.value.
Recalculation
Vue remembers which reactive data the function read. When that data changes, the value is recalculated by itself:
<script setup>
import { ref, computed } from 'vue'
const todos = ref([
{ id: 1, text: 'Buy bread', done: false },
{ id: 2, text: 'Call Anna', done: false },
])
const remaining = computed(() => todos.value.filter((t) => !t.done).length)
function finishFirst() {
todos.value[0].done = true
}
</script>
<template>
<p>Tasks left: {{ remaining }}</p>
<button @click="finishFirst">Finish the first one</button>
</template>At first the page shows "Tasks left: 2", after the click — "1".
A filtered list
computed is used most often for lists shown in a different form:
<script setup>
import { ref, computed } from 'vue'
const todos = ref([
{ id: 1, text: 'Buy bread', done: true },
{ id: 2, text: 'Call Anna', done: false },
])
const onlyActive = ref(false)
const visibleTodos = computed(() => {
return onlyActive.value
? todos.value.filter((t) => !t.done)
: todos.value
})
</script>visibleTodos depends on both the list and the onlyActive flag. The v-for directive from the lists lesson will put such a list on the page.
Why computed beats a function
A function called in the template gives the same result: {{ countRemaining() }}. The difference is caching:
| Function | computed | |
|---|---|---|
| When it runs | On every re-render | Only when its dependencies change |
| In the template | countRemaining() | remaining |
If something unrelated to tasks changes on the page, the function still runs again, while computed returns the stored value.
Common mistake
Changing state inside computed:
<script setup>
const sorted = computed(() => todos.value.sort((a, b) => a.id - b.id))
</script>sort reorders the original array — the computing function changes the very data it depends on. A computed property should only read and return. Sort a copy: [...todos.value].sort(...) or todos.value.toSorted(...).