Vue Watchers
The watch option lets a component react to a specific piece of state changing, giving you both the old and new value to act on.
What Watchers Are For
A watcher observes a reactive source and runs a callback whenever that source changes. Watchers are the right tool for side effects that a computed property should never perform - logging, calling an API, syncing a value to localStorage, or resetting some unrelated piece of state in response to a change.
Declaring a Basic Watcher
The watch option is an object whose keys match the name of a data property, prop, or computed property. Each value is a handler function that Vue calls with two arguments: the new value first, then the old value.
A basic watcher with old and new values
<div id="app">
<input v-model="searchTerm" placeholder="Search users..." />
<p>Last search logged: {{ lastLogged }}</p>
</div>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
const app = Vue.createApp({
data() {
return {
searchTerm: '',
lastLogged: ''
}
},
watch: {
searchTerm(newValue, oldValue) {
console.log('Search changed from "' + oldValue + '" to "' + newValue + '"')
this.lastLogged = newValue
}
}
})
app.mount('#app')
</script>Watching Nested Data with deep and immediate
By default, watch only detects a fresh assignment to the source - reassigning profile = { ...profile, age: 31 } triggers it, but mutating profile.age directly does not. Setting deep: true tells Vue to walk into nested objects and arrays and fire the handler on any mutation inside them. Setting immediate: true runs the handler once right away when the watcher is created, not only after the first future change.
Object form with deep and immediate
<div id="app">
<button @click="profile.age++">Birthday (+1 year)</button>
<p>Age: {{ profile.age }}</p>
<p>Change log entries: {{ changeLog.length }}</p>
</div>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
const app = Vue.createApp({
data() {
return {
profile: { name: 'Sam', age: 30 },
changeLog: []
}
},
watch: {
profile: {
handler(newProfile) {
this.changeLog.push('Profile updated, age is now ' + newProfile.age)
},
deep: true,
immediate: true
}
}
})
app.mount('#app')
</script>- A watcher's handler always receives two arguments: the new value first, then the old value.
- By default, watch only detects a fresh assignment to the source; mutating properties inside an object requires deep: true.
- Setting immediate: true runs the handler once right away, in addition to every future change.
- You can watch a dotted-path string like 'settings.theme.fontSize' to observe one nested property without watching the whole object.
- Computed properties can be watched too, since they are just another reactive source.
Watching a Nested Path and a Computed Property
Instead of deep-watching an entire object, you can watch a single nested value by passing its dotted path as a string key. Watch sources are not limited to data properties either - a computed property is a perfectly valid watch target, since it is reactive just like any other source.
Watching a dotted path and a computed property
<div id="app">
<input v-model.number="settings.theme.fontSize" type="number" />
<p>Font size doubled: {{ doubledFontSize }}</p>
</div>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
const app = Vue.createApp({
data() {
return {
settings: { theme: { fontSize: 14 } }
}
},
computed: {
doubledFontSize() {
return this.settings.theme.fontSize * 2
}
},
watch: {
'settings.theme.fontSize'(newSize, oldSize) {
console.log('Font size changed from', oldSize, 'to', newSize)
},
doubledFontSize(newVal) {
console.log('Doubled font size is now', newVal)
}
}
})
app.mount('#app')
</script>Exercise: Vue Watchers
When does a watch callback actually execute relative to the watched data changing?