Computed Properties and Watchers
Chapter 2 put a {{ price * quantity }} expression directly in the template. That works, but repeating it or running heavier logic inline gets messy fast. Computed properties are Vue's clean way to derive a reactive value from other reactive values; watchers are for running a side effect when something changes. Both will feel familiar from React's useMemo and useEffect — but with Vue's automatic dependency tracking.
computed — A Derived Reactive Value
computed(() => ...) returns a ref-like value that automatically recalculates whenever any reactive value it reads changes — here, whenever price or quantity changes. Two important properties: it's cached (only recomputes when a dependency actually changes, not on every render), and its dependencies are auto-tracked — there's no dependency array to maintain, unlike React's useMemo. Vue knows total depends on price and quantity simply because the function read them. In the template it's used as {{ total }} (auto-unwrapped, no .value); in script you'd read total.value.
computed() signal felt clean, this is the same idea — a derived value that tracks its own dependencies and caches. Vue's computed predates Angular's by years; the convergence is no accident, as both frameworks moved toward fine-grained reactivity. Coming from React, think "useMemo with no dependency array, and you read it like a value."
Computed vs a Method
You could call a regular method in the template instead — but a method re-runs every time the component re-renders for any reason, even if its inputs didn't change. A computed caches its result and only recomputes when a dependency genuinely changes. For anything beyond a trivial expression, prefer computed — the caching is free performance.
Filtering a List with computed
This is the clean fix for Chapter 4's "don't combine v-if and v-for" rule: a computed property returns exactly the items that should be shown, and the template does v-for="todo in visibleTodos". The filter recomputes automatically when either todos or showCompleted changes — the same "filter before mapping" pattern React used in Fundamentals Chapter 6, here expressed reactively.
watch — Running a Side Effect on Change
watch runs a callback whenever a specific reactive source changes, receiving both the new and old value. It's for genuine side effects in response to a change — fetching data, saving to localStorage, logging — the role React's useEffect with a dependency plays. The key distinction from computed: computed derives and returns a value; watch does something and returns nothing. If you find yourself assigning to another ref inside a watch, a computed is usually the better tool.
watchEffect — Auto-Tracked Watching
watchEffect is a variant that runs immediately and re-runs whenever any reactive value it reads changes — automatically tracked, no explicit source to name. It's closer in feel to React's useEffect with auto-detected dependencies. Use watch when you need the old value or want to watch one specific source; use watchEffect when you just want "re-run this whenever anything it uses changes."
computed in disguise, and the computed version is simpler, cached, and less bug-prone. Reserve watch/watchEffect for true side effects (I/O, logging, imperative DOM work). The mental rule: "deriving a value → computed; doing something → watch."
| Vue | React | Angular |
|---|---|---|
| computed(() => ...) | useMemo (no dep array) | computed() signal |
| watch(src, cb) | useEffect with a dep | effect() / RxJS watch |
| watchEffect(cb) | useEffect (auto deps) | effect() signal |
Coding Challenges
Build a component with firstName and lastName refs and a fullName computed property combining them, with inputs to edit each — confirming fullName updates automatically.
📄 View solutionBuild a todo list with a "show completed" checkbox, using a computed visibleTodos that filters based on the checkbox, then v-for over visibleTodos — the clean fix for not combining v-if and v-for.
📄 View solutionBuild a component with a count ref and a watch that logs the new and old value whenever count changes, plus a watchEffect that logs a message referencing count — observing how the two differ (watchEffect runs immediately, watch does not).
📄 View solutionChapter 5 Quick Reference
- computed(() => ...) — a cached, auto-tracked derived value (=
useMemowith no dep array) - Prefer
computedover a method for derived values — it caches; a method re-runs every render - A
computedfilter is the clean fix for "don't combine v-if and v-for" (Chapter 4) - watch(source, (new, old) => ...) — runs a side effect on change, with old/new values
- watchEffect(() => ...) — runs immediately and re-runs on any read dependency's change
- Rule of thumb: deriving a value → computed; doing something → watch
- Next chapter: components and props