Vue.js Reactivity System
Vue's reactivity system is the core feature that makes data changes automatically update the user interface. Understanding reactivity is essential for building efficient and responsive Vue.js applications.
Available Reactivity Functions
Core Reactivity
ref()- Create reactive references for primitive values and objects
Additional Reactivity Functions
reactive()- Create reactive objects and arrayscomputed()- Create computed properties that update automaticallywatch()- Watch reactive data for changeswatchEffect()- Automatically track dependencies and run effects
Understanding Reactivity
What is Reactivity?
Reactivity means that when data changes, the UI automatically updates to reflect those changes. Vue achieves this through a sophisticated dependency tracking system.
import { ref } from 'vue'
// Create reactive data
const count = ref(0)
// When count changes, any template using it will update automatically
const increment = () => {
count.value++ // UI updates automatically
}
Reactive vs Non-Reactive Data
// ✅ Reactive - UI will update
const reactiveCount = ref(0)
const reactiveUser = reactive({ name: 'John' })
// ❌ Non-reactive - UI won't update
let plainCount = 0
const plainUser = { name: 'John' }
Core Concepts
Refs vs Reactive
Use ref() for:
- Primitive values (strings, numbers, booleans)
- When you need to replace the entire value
- Single values that might change type
const count = ref(0)
const message = ref('Hello')
const isVisible = ref(true)
Use reactive() for:
- Objects and arrays
- When you want to mutate properties
- Complex data structures
const user = reactive({
name: 'John',
age: 30,
preferences: {
theme: 'dark'
}
})
Accessing Reactive Values
In JavaScript (script):
// Refs need .value
console.log(count.value) // 0
count.value = 10
// Reactive objects don't need .value
console.log(user.name) // 'John'
user.name = 'Jane'
In Templates:
<template>
<!-- Refs are automatically unwrapped -->
<p>Count: {{ count }}</p>
<!-- Reactive objects work directly -->
<p>Name: {{ user.name }}</p>
</template>
Common Patterns
Form Data Management
const form = reactive({
name: '',
email: '',
message: '',
errors: {}
})
const validateForm = () => {
form.errors = {}
if (!form.name) form.errors.name = 'Name is required'
if (!form.email) form.errors.email = 'Email is required'
}
Loading States
const loading = ref(false)
const data = ref(null)
const error = ref(null)
const fetchData = async () => {
loading.value = true
error.value = null
try {
const response = await api.getData()
data.value = response.data
} catch (err) {
error.value = err.message
} finally {
loading.value = false
}
}
Computed Properties
const firstName = ref('John')
const lastName = ref('Doe')
// Automatically updates when firstName or lastName changes
const fullName = computed(() => {
return `${firstName.value} ${lastName.value}`
})
Watchers
// Watch a single ref
watch(count, (newValue, oldValue) => {
console.log(`Count changed from ${oldValue} to ${newValue}`)
})
// Watch multiple sources
watch([firstName, lastName], ([newFirst, newLast]) => {
console.log(`Name changed to ${newFirst} ${newLast}`)
})
// Watch reactive object properties
watch(() => user.name, (newName) => {
console.log(`User name changed to ${newName}`)
})
Advanced Reactivity
Shallow Reactivity
import { shallowRef, shallowReactive } from 'vue'
// Only the reference is reactive, not nested properties
const shallowUser = shallowRef({ name: 'John', age: 30 })
// Only first-level properties are reactive
const shallowState = shallowReactive({
count: 0,
nested: { value: 1 } // This won't be reactive
})
Raw Objects
import { markRaw } from 'vue'
// Prevent an object from being made reactive
const rawObject = markRaw({
data: 'This will never be reactive'
})
Performance Tips
- Use
shallowRef()for large objects that don't need deep reactivity - Use
markRaw()for objects that should never be reactive (like external libraries) - Prefer
computed()over methods for derived data - Use
readonly()to prevent accidental mutations - Consider
shallowReactive()for performance-critical scenarios