Vue Watchers
Reacting to data changes in Vue
👁️ What are Watchers?
Watchers observe data changes and perform actions when specific properties change. They're perfect for asynchronous operations, API calls, or complex logic triggered by data updates.
<div id="app">
<input v-model="question" placeholder="Ask a question">
<p>{{ answer }}</p>
</div>
<script src="https://unpkg.com/vue@3"></script>
<script>
Vue.createApp({
data() {
return {
question: '',
answer: 'Ask a question!'
}
},
watch: {
question(newVal) {
this.answer = 'Thinking...'
}
}
}).mount('#app')
</script>
Watcher Features
Observe Changes
Watch specific data properties for changes
Async Operations
Perfect for API calls and delays
Deep Watching
Watch nested object properties
Immediate Execution
Run watcher immediately on mount
🔹 Basic Watcher
Watchers are defined in the watch option. They receive the new value and old value as parameters when the watched property changes.
<div id="app">
<input v-model="message" placeholder="Type something">
<p>Message: {{ message }}</p>
<p>Character count: {{ charCount }}</p>
</div>
<script src="https://unpkg.com/vue@3"></script>
<script>
Vue.createApp({
data() {
return {
message: '',
charCount: 0
}
},
watch: {
message(newValue, oldValue) {
console.log('Changed from:', oldValue, 'to:', newValue)
this.charCount = newValue.length
}
}
}).mount('#app')
</script>
Output:
Message:
Character count: 0
🔹 Watchers vs Computed Properties
While both react to data changes, they serve different purposes. Use computed for synchronous calculations and watchers for asynchronous or expensive operations.
When to Use Each:
- Computed Properties: Synchronous calculations, derived data, template display
- Watchers: Asynchronous operations, API calls, side effects, complex logic
<div id="app">
<input v-model="searchTerm" placeholder="Search...">
<p>{{ searchStatus }}</p>
</div>
<script src="https://unpkg.com/vue@3"></script>
<script>
Vue.createApp({
data() {
return {
searchTerm: '',
searchStatus: 'Ready to search'
}
},
watch: {
searchTerm(newTerm) {
if (newTerm.length > 0) {
this.searchStatus = 'Searching...'
// Simulate API call
setTimeout(() => {
this.searchStatus = `Found results for "${newTerm}"`
}, 1000)
} else {
this.searchStatus = 'Ready to search'
}
}
}
}).mount('#app')
</script>
🔹 Deep Watching Objects
By default, watchers only observe direct property changes. Use the deep option to watch nested properties inside objects.
<div id="app">
<input v-model="user.name" placeholder="Name">
<input v-model.number="user.age" type="number" placeholder="Age">
<p>{{ changeLog }}</p>
</div>
<script src="https://unpkg.com/vue@3"></script>
<script>
Vue.createApp({
data() {
return {
user: {
name: 'John',
age: 25
},
changeLog: 'No changes yet'
}
},
watch: {
user: {
handler(newVal) {
this.changeLog = `User updated: ${newVal.name}, ${newVal.age}`
},
deep: true
}
}
}).mount('#app')
</script>
Output:
No changes yet
🔹 Immediate Watchers
Use the immediate option to run the watcher immediately when the component is created, not just when the value changes.
<div id="app">
<p>{{ status }}</p>
<button @click="count++">Count: {{ count }}</button>
</div>
<script src="https://unpkg.com/vue@3"></script>
<script>
Vue.createApp({
data() {
return {
count: 0,
status: ''
}
},
watch: {
count: {
handler(newVal) {
this.status = `Count is now: ${newVal}`
},
immediate: true // Runs on mount
}
}
}).mount('#app')
</script>
Output:
Count is now: 0
🔹 Practical Example: Form Validation
Watchers are great for real-time form validation with debouncing to avoid excessive checks:
<div id="app">
<label>Username:</label>
<input v-model="username" placeholder="Enter username">
<p :style="{ color: validationColor }">{{ validationMessage }}</p>
</div>
<script src="https://unpkg.com/vue@3"></script>
<script>
Vue.createApp({
data() {
return {
username: '',
validationMessage: '',
validationColor: 'black',
timeout: null
}
},
watch: {
username(newVal) {
// Clear previous timeout
clearTimeout(this.timeout)
if (newVal.length === 0) {
this.validationMessage = ''
return
}
this.validationMessage = 'Checking...'
this.validationColor = 'orange'
// Debounce validation
this.timeout = setTimeout(() => {
if (newVal.length < 3) {
this.validationMessage = 'Too short (min 3 characters)'
this.validationColor = 'red'
} else if (newVal.length > 15) {
this.validationMessage = 'Too long (max 15 characters)'
this.validationColor = 'red'
} else {
this.validationMessage = 'Username available!'
this.validationColor = 'green'
}
}, 500)
}
}
}).mount('#app')
</script>
Output:
🔹 Watching Multiple Properties
You can watch multiple properties by defining separate watchers for each, or watch computed properties that depend on multiple values:
<div id="app">
<input v-model.number="width" type="number" placeholder="Width">
<input v-model.number="height" type="number" placeholder="Height">
<p>Area: {{ area }}</p>
<p>{{ sizeMessage }}</p>
</div>
<script src="https://unpkg.com/vue@3"></script>
<script>
Vue.createApp({
data() {
return {
width: 10,
height: 10,
sizeMessage: ''
}
},
computed: {
area() {
return this.width * this.height
}
},
watch: {
area(newArea) {
if (newArea < 50) {
this.sizeMessage = 'Small area'
} else if (newArea < 200) {
this.sizeMessage = 'Medium area'
} else {
this.sizeMessage = 'Large area'
}
}
}
}).mount('#app')
</script>
Output:
Area: 100
Medium area