codejump

AcademyVue

Computed Properties: Cached, Derived State

A computed only recalculates when a dependency it read actually changes — the caching a plain method call can never give you.

3 min readUpdated

3 exercises

Waiting at the end of this page.

A method re-runs on every render, for any reason

Call a method from a template and it runs every single time that template re-renders — whether or not anything the method actually reads has changed:

vue
<script setup lang="ts">
import { ref } from 'vue'

const items = ref([{ price: 10, quantity: 2 }, { price: 25, quantity: 1 }])
const unrelated = ref(0)

function subtotal() {
  console.log('subtotal recalculated')
  return items.value.reduce((sum, item) => sum + item.price * item.quantity, 0)
}
</script>

<template>
  <p>{{ subtotal() }}</p>
  <button @click="unrelated++">Unrelated click</button>
</template>

Clicking the unrelated button re-renders the component, and subtotal() logs again — it has no idea "unrelated" has nothing to do with it. For a cheap sum over two items this barely matters; for anything expensive, called from several places in a template, it adds up fast.

computed() only recalculates when its own dependencies change

vue
<script setup lang="ts">
import { ref, computed } from 'vue'

const items = ref([{ price: 10, quantity: 2 }, { price: 25, quantity: 1 }])
const unrelated = ref(0)

const subtotal = computed(() => {
  console.log('subtotal recalculated')
  return items.value.reduce((sum, item) => sum + item.price * item.quantity, 0)
})
</script>

<template>
  <p>{{ subtotal }}</p>
  <button @click="unrelated++">Unrelated click</button>
</template>

Two changes, and both matter: subtotal is accessed like a ref ({{ subtotal }}, no ()), and the log only fires the first time it is read and again whenever items actually changes — clicking "Unrelated click" a hundred times logs nothing. Vue tracks exactly which reactive values a computed's getter read the last time it ran, and only reruns the getter when one of those specific values changes.

The trade computed makes: reads are cheap, writes stay pure

A computed getter should be a pure function of the state it reads — no mutating other refs, no side effects beyond its own internal logging-for-demo-purposes. Vue may call the getter zero times (if nothing ever reads it), once, or many times as dependencies change, and it may also re-evaluate it lazily rather than eagerly the instant a dependency changes — code inside a computed that assumes "this runs exactly once per state change, right when the state changes" is code built on an assumption Vue does not promise.

Read-write computed, for the rarer case

A computed is usually read-only, but it can also accept a setter, useful for a two-way-bindable derived value:

JavaScript
const firstName = ref('Ada')
const lastName = ref('Lovelace')

const fullName = computed({
  get: () => `${firstName.value} ${lastName.value}`,
  set: (value) => {
    const [first, last] = value.split(' ')
    firstName.value = first
    lastName.value = last
  },
})

fullName.value = 'Grace Hopper'
// firstName.value === 'Grace', lastName.value === 'Hopper'

This is the shape that makes a computed usable with v-model on a component prop pair — reading returns the derived value, writing routes back into whatever the derived value was built from.

What interviewers check

Whether "computed vs method" gets an answer about caching, not phrasing — a computed and a method with the exact same body produce the exact same output, every time. The difference is entirely about how many times the body actually runs, and that difference is invisible unless you ask specifically about it.

Now practice it

2 quizzes · 1 drill

Reading this page is the cheap half. These are the exercises that make you use it.