Roadmap › Vue.js 3 (with Pinia, Vue Router & Nuxt basics) › Module: Reactivity
Lessons in this course (17)
5. ref, reactive, computed and watch
Basic★ Must knowLesson 5 of 17
ts
import { ref, reactive, computed, watch, watchEffect } from "vue";
const count = ref(0); // any value; access .value in script, auto-unwrapped in templates
count.value++;
const cart = reactive({ items: [] as Item[], coupon: "" }); // objects only; deep reactive Proxy
cart.items.push({ id: 1, price: 900, qty: 2 });
const total = computed(() => cart.items.reduce((s, i) => s + i.price * i.qty, 0)); // cached until deps change
watch(count, (newVal, oldVal) => console.log(`count ${oldVal} → ${newVal}`)); // specific source, lazy
watch(() => cart.coupon, async code => { cart.discount = await validateCoupon(code); });
watch(() => props.userId, id => loadUser(id), { immediate: true });
watchEffect(() => console.log("total is", total.value)); // runs immediately, auto-tracks dependenciesref | reactive | |
|---|---|---|
| Holds | Any type (primitives too) | Objects, arrays, Maps, Sets |
| Access | .value in script | Direct property access |
| Reassign whole value | Yes (x.value = newObj) | No — replacing the variable breaks reactivity |
| Destructuring | Fine | Loses reactivity — use toRefs() |
Recommendation from the Vue docs: prefer ref as the primary API. shallowRef/shallowReactive for large non-deep data; readonly() to expose state safely; toRaw()/markRaw() to opt out.
computed vs watch vs methods: computed = derived value, cached; methods = called every time (event handlers or uncached work); watch = side effects in response to changes (API calls, localStorage).