On this page
Reactivity Fundamentals
Vue’s reactivity system tracks data changes and automatically updates the DOM. In the Composition API, you create reactive state with ref, reactive, computed, and watch.
ref — Reactive Primitives
Use ref() for any value. Access and mutate it with .value in script, but not in templates:
<script setup>
import { ref } from 'vue';
const count = ref(0);
const name = ref('Alice');
const isActive = ref(true);
function increment() {
count.value++;
}
</script>
<template>
<p>{{ name }} clicked {{ count }} times</p>
<button @click="increment">+1</button>
</template>
reactive — Reactive Objects
Use reactive() for objects and arrays. No .value needed:
<script setup>
import { reactive } from 'vue';
const user = reactive({
name: 'Bob',
age: 30,
address: { city: 'Portland' },
});
function birthday() {
user.age++;
user.address.city = 'Seattle';
}
</script>
<template>
<p>{{ user.name }}, age {{ user.age }} — {{ user.address.city }}</p>
<button @click="birthday">Birthday</button>
</template>
Tip: Prefer ref for primitives and when reassigning entire objects. Use reactive for stable object shapes.
computed — Derived State
computed creates cached, derived values that update when dependencies change:
<script setup>
import { ref, computed } from 'vue';
const firstName = ref('Jane');
const lastName = ref('Doe');
const fullName = computed(() => `${firstName.value} ${lastName.value}`);
const items = ref([10, 20, 30]);
const total = computed(() => items.value.reduce((sum, n) => sum + n, 0));
</script>
<template>
<p>{{ fullName }}</p>
<p>Total: {{ total }}</p>
</template>
watch — Side Effects
React to data changes with watch:
<script setup>
import { ref, watch } from 'vue';
const searchQuery = ref('');
const results = ref([]);
watch(searchQuery, async (newQuery) => {
if (newQuery.length < 2) {
results.value = [];
return;
}
const response = await fetch(`/api/search?q=${newQuery}`);
results.value = await response.json();
});
</script>
<template>
<input v-model="searchQuery" placeholder="Search..." />
<ul>
<li v-for="item in results" :key="item.id">{{ item.name }}</li>
</ul>
</template>
watchEffect — Automatic Tracking
watchEffect runs immediately and re-runs when any tracked dependency changes:
import { ref, watchEffect } from 'vue';
const count = ref(0);
watchEffect(() => {
document.title = `Count: ${count.value}`;
});
ref vs reactive Summary
| API | Best for | Access in script |
|---|---|---|
ref |
Primitives, reassignable values | .value |
reactive |
Objects, nested data | Direct property access |
computed |
Derived values | .value (script) |
watch |
Side effects on change | — |
Next: organize UI into reusable components with props, emits, and slots.