Computed properties and watchers both react to reactive changes, but they solve different problems: computed is for derived state (cached, declarative), while watch is for side effects (async/imperative work triggered by changes).
Use this Vue interview question to rehearse a quick answer, common mistake, follow-up, and production pitfall.
Computed vs watch in Vue: derived state (cached) vs side effects (imperative)Frontend interview answer
This Vue interview question tests whether you can explain the Difference Between computed and watch in Vue, connect it to production trade-offs, and handle common follow-up questions.
- the Difference Between computed and watch in Vue explanation without falling back to memorized docs wording
- Computed and Watchers reasoning, edge cases, and production failure modes
- How you would answer the most likely Vue interview follow-up
Core idea
Computed = a derived value (like a formula). Vue tracks its dependencies and caches the result until one of them changes.
Watch = run code when something changes. It’s meant for side effects (fetching, logging, syncing, timers), not for producing values for the template.
Aspect | computed | watch / watchEffect |
|---|---|---|
Primary purpose | Derived state (calculate a value from other reactive state) | Side effects (do something when state changes) |
Caching | Yes (cached until deps change) | No (runs when triggered) |
Evaluation model | Lazy: recomputes when accessed after being invalidated | Eager: runs callback when source changes (timing configurable via flush) |
Return value | Yes (you read it like a value) | No (callback-driven; produces effects, not a value) |
Async work | Avoid (keep it pure) | Yes (common: API calls + cancellation) |
Common mistake | Putting side effects in computed | Using watch to keep derived state in sync (duplicate state) |
Anti-pattern (common in interviews): using watch for derived state
This duplicates state and can drift out of sync. Prefer computed unless you truly need an effect.
<!-- BAD: derived state via watch (duplicate state) -->
<script setup>
import { ref, watch, computed } from 'vue';
const first = ref('Mina');
const last = ref('Yilmaz');
const fullNameViaWatch = ref('');
watch([first, last], ([f, l]) => {
fullNameViaWatch.value = `${f} ${l}`;
}, { immediate: true });
// GOOD: derived state via computed (cached, always in sync)
const fullName = computed(() => `${first.value} ${last.value}`);
</script>
<template>
<p>watch-derived: {{ fullNameViaWatch }}</p>
<p>computed: {{ fullName }}</p>
</template>
Watchers (the right use): side effects + async + cancellation
When a value changes and you need to do something (fetch, analytics, sync URL, write to storage), use watch. Use cleanup to avoid race conditions (old request finishing after a new one).
<script setup>
import { ref, watch } from 'vue';
const query = ref('');
const results = ref([]);
const error = ref(null);
watch(query, async (q, _prev, onCleanup) => {
if (!q.trim()) {
results.value = [];
error.value = null;
return;
}
const ctrl = new AbortController();
onCleanup(() => ctrl.abort());
try {
error.value = null;
const res = await fetch(`/api/search?q=${encodeURIComponent(q)}`, { signal: ctrl.signal });
results.value = await res.json();
} catch (e) {
// Ignore abort; handle real errors
if (e?.name !== 'AbortError') error.value = e;
}
}, { flush: 'post' });
</script>
<template>
<input v-model="query" placeholder="Search..." />
<pre v-if="error">{{ error }}</pre>
<ul>
<li v-for="r in results" :key="r.id">{{ r.title }}</li>
</ul>
</template>
Key options you should know (interview-level)
immediate: trueruns the watcher once on setup (useful for initial fetch).deep: truewatches nested mutations (use sparingly; can be expensive). Prefer watching a specific getter like() => obj.a.b.flush:'pre'(default),'post'(after DOM updates),'sync'(runs immediately; use carefully).watchEffect(): tracks dependencies automatically (great for effects that depend on many reactive reads), but use it only for effects (same rule: not for derived display values).
Rule of thumb
If the result is a value you want to render (filtering, formatting, totals) → computed.
If you need to do something when it changes (fetch, sync, log, imperative updates) → watch.
Use this as one explanation rep, then continue with the Vue.js interview questions cluster or a guided prep path.