Vue.js Watchers
By Flavio Copes
Learn how Vue.js watchers let you spy on a single piece of component state and run a function whenever that property value changes, with Options and Composition API.
A watcher is a special Vue.js feature that allows you to spy on one property of the component state, and run a function when that property value changes. This guide is for Vue 3.
Watchers are made for side effects: logging, calling an API, saving to localStorage. If all you need is a value derived from other values, use a computed property instead. Reach for a watcher when a change must trigger an action.
Here’s an example. We have a component that shows a name, and allows you to change it by clicking a button:
<template>
<div>
<p>My name is {{ name }}</p>
<button @click="changeName()">Change my name!</button>
</div>
</template>
<script setup>
import { ref } from 'vue'
const name = ref('Flavio')
function changeName() {
name.value = 'Flavius'
}
</script>
When the name changes we want to do something, like printing a console log.
With Composition API, import watch from vue:
<script setup>
import { ref, watch } from 'vue'
const name = ref('Flavio')
function changeName() {
name.value = 'Flavius'
}
watch(name, (newValue, oldValue) => {
console.log(newValue, oldValue)
})
</script>
Click the button and the console prints Flavius (and the previous value).
With Options API, add to the watch object a property named as the data property we want to watch over:
<script>
export default {
data() {
return {
name: 'Flavio'
}
},
methods: {
changeName() {
this.name = 'Flavius'
}
},
watch: {
name(newValue, oldValue) {
console.log(newValue, oldValue)
}
}
}
</script>
The handler can optionally accept 2 parameters. The first is the new property value. The second is the old property value.
Watchers cannot be looked up from a template as you can with computed properties. For calling functions from clicks and other DOM events, see Vue methods.
Running the watcher immediately
By default a watcher only runs when the value changes. It does not run when the component is created with its initial value. This trips people up when they expect the watcher to fire on load.
With Composition API, pass { immediate: true } as the third argument:
watch(name, (newValue, oldValue) => {
console.log(newValue, oldValue)
}, { immediate: true })
With Options API, use the object form:
watch: {
name: {
handler(newValue, oldValue) {
console.log(newValue, oldValue)
},
immediate: true
}
}
Now the handler also runs once at creation, with the initial value as newValue and undefined as oldValue.
Watching objects
Another gotcha: if you watch an object, the watcher fires when the object is replaced, not when one of its nested properties changes. To catch nested changes, add deep: true:
Composition API:
watch(user, (newValue) => {
console.log(newValue.name)
}, { deep: true })
Options API:
watch: {
user: {
handler(newValue) {
console.log(newValue.name)
},
deep: true
}
}
Deep watching walks the whole object on every change, so keep it for small objects.
Want me to talk about your product? You can sponsor this site.