SyntaxStudy
Sign Up
Vue.js Computed vs Methods vs Watchers
Vue.js Beginner 1 min read

Computed vs Methods vs Watchers

A common question for newcomers is when to use a computed property versus a method versus a watcher. Methods are best for event handlers and operations with side effects — they run every time they're called with no caching. Computed properties are best for values derived from reactive state that are accessed in the template — they run only when dependencies change and are cached between renders. Watchers are callbacks triggered by state changes, designed for side effects: API calls, local storage writes, imperative DOM manipulation, or logging. Unlike computed properties, watchers don't produce a return value used in the template. If you find yourself writing a watcher that only transforms data and assigns it to another ref, that logic almost certainly belongs in a computed property instead. A rule of thumb: if you can express it as a pure derivation (`if this state, then this value`), use `computed`. If you need to react to a change and do something with a side effect (`when this changes, do that`), use `watch` or `watchEffect`.
Example
<script setup>
import { ref, computed, watch } from 'vue';

const search  = ref('');
const rawList = ref(['Apple', 'Apricot', 'Banana', 'Blueberry', 'Cherry']);

// ── computed: cached filtered list ────────────
const filtered = computed(() =>
  rawList.value.filter(item =>
    item.toLowerCase().includes(search.value.toLowerCase())
  )
);

// ── method: always re-runs (no cache) ─────────
function getFiltered() {
  return rawList.value.filter(item =>
    item.toLowerCase().includes(search.value.toLowerCase())
  );
}

// ── watcher: side effect on change ────────────
const searchLog = ref([]);
watch(search, (newVal) => {
  // Side effect — not a derived value
  searchLog.value.push({ query: newVal, ts: Date.now() });
  // Could also debounce + call API here
});
</script>

<template>
  <input v-model="search" placeholder="Search fruit…" />
  <!-- Use computed in template — cached: -->
  <li v-for="f in filtered" :key="f">{{ f }}</li>
  <p>{{ searchLog.length }} searches logged</p>
</template>