Computed Properties
Shelf's header says "Finished 3 of 8" and the sidebar lists only unread books. Both are calculated from the same books array. You could write the filter directly in the template, twice, and have it rerun on every render. Or you can name the result once and let Vue remember it.
The rule
Computed derives a value, caches it, and recalculates only when its inputs change.
<script setup>
import { ref, computed } from 'vue'
const books = ref([
{ title: 'Dune', pages: 412, read: true },
{ title: 'Circe', pages: 393, read: false },
{ title: 'Piranesi', pages: 245, read: false }
])
const unread = computed(() => books.value.filter(b => !b.read))
const finishedCount = computed(() => books.value.length - unread.value.length)
</script>
<template>
<h2>Finished {{ finishedCount }} of {{ books.length }}</h2>
<ul>
<li v-for="b in unread" :key="b.title">{{ b.title }}</li>
</ul>
</template>What each part of the rule means:
- Derives. You pass a getter, a function that returns a value.
computedreturns a read-only ref, so script code readsunread.valueand the template readsunread. - Caches. Vue records which reactive data the getter touched, here
books. Until that data changes, every read returns the stored result without running the getter again. - Recalculates on change. Flip a book's
readflag and both computeds refresh; the header and list update.
Compare a method: function unreadList() { return books.value.filter(...) } called as {{ unreadList() }} gives the same output, but runs on every re-render of the component, even when something unrelated changed.
Rules for a good getter:
- No side effects. Do not change other state, fetch data or touch the DOM inside it. It should only calculate. Side effects belong in watchers (Lifecycle Hooks section).
- Depend on reactive data.
computed(() => Date.now())is computed once and never changes, becauseDate.now()is not reactive. - Treat the result as read-only. To make one writable you must supply both
getandset; that is rare.
The shortcut
Ask: "Is this a value I could describe as a noun, built from other state?" "Unread books", "finished count", "full title" → computed. "Save the list", "load a cover" → not a value; that is an action or a side effect.
Is it a noun built from state?
Yes: the total pages of unread books.
Drills
Answer fast — trust the rule.
What is the key advantage of computed over calling a method in the template?
In <script setup>, how do you read the computed unread?
Which getter breaks the rules for computed?
Why does computed(() => Date.now()) never update?
Remember
computed(() => ...)returns a cached, read-only ref derived from reactive state.- Shortcut: a noun built from state is a computed; an action is not.
- Trap: side effects in the getter, or depending on non-reactive values.
