Vue Data and Methods -> Watchers
Watchers in Vue.js provide a way to react to changes in your data. They allow you to execute a function whenever a specific data property is modified. This is incredibly useful for performing side effects, like making API calls, updating computed properties, or manipulating the DOM, in response to data changes.
Basic Watchers
The most common way to define a watcher is within the watch option of your Vue component.
<template>
<div>
<p>Message: {{ message }}</p>
<button @click="changeMessage">Change Message</button>
</div>
</template>
<script>
export default {
data() {
return {
message: 'Hello Vue!'
}
},
methods: {
changeMessage() {
this.message = 'New Message!';
}
},
watch: {
message(newValue, oldValue) {
console.log(`Message changed from ${oldValue} to ${newValue}`);
// Perform side effects here, e.g., API call, DOM manipulation
}
}
}
</script>
Explanation:
watch: The option where you define watchers.message: The name of the data property you want to watch. This must match the property name in yourdataoption.(newValue, oldValue): The handler function that will be executed whenmessagechanges. It receives two arguments:newValue: The new value of themessageproperty.oldValue: The previous value of themessageproperty.
Deep Watching
By default, watchers are not deep. This means that if you're watching an object or array, the watcher will only trigger if the object/array itself is replaced, not if its properties are modified. To watch for changes within an object or array, you need to use the deep option.
<template>
<div>
<p>Object: {{ myObject }}</p>
<button @click="updateObject">Update Object</button>
</div>
</template>
<script>
export default {
data() {
return {
myObject: {
name: 'Initial Name',
age: 30
}
}
},
methods: {
updateObject() {
this.myObject.age = 31; // Modifying a property, not replacing the object
}
},
watch: {
myObject: {
handler(newValue, oldValue) {
console.log('Object changed:', newValue, oldValue);
},
deep: true // Enable deep watching
}
}
}
</script>
Explanation:
deep: true: This option tells Vue to recursively watch all properties of themyObject. Now, whenmyObject.ageis updated, the watcher will be triggered.
Immediate Execution
Sometimes you want the watcher to run immediately when the component is created, even before the data property changes. You can achieve this using the immediate option.
<template>
<div>
<p>Count: {{ count }}</p>
</div>
</template>
<script>
export default {
data() {
return {
count: 0
}
},
watch: {
count: {
handler(newValue, oldValue) {
console.log('Count is now:', newValue);
},
immediate: true // Run the handler immediately
}
}
}
</script>
Explanation:
immediate: true: Thehandlerfunction will be called once when the component is created, with the initial value ofcount(which is 0 in this case).
Watcher as an Object
As shown in the examples above, you can define a watcher as an object with handler, deep, and immediate options. This provides more control over the watcher's behavior.
Using $watch Programmatically
You can also create watchers programmatically using the $watch instance method. This is useful when you need to create watchers dynamically.
<template>
<div>
<p>Value: {{ value }}</p>
<button @click="startWatching">Start Watching</button>
</div>
</template>
<script>
export default {
data() {
return {
value: 10
}
},
methods: {
startWatching() {
this.$watch('value', (newValue, oldValue) => {
console.log('Value changed (programmatically):', newValue, oldValue);
}, {
immediate: true,
deep: false // Optional: specify deep watching
});
}
}
}
</script>
Explanation:
this.$watch('value', callback, options):'value': The data property to watch.callback: The function to execute when the property changes.options: An optional object withimmediateanddeepoptions.
Unwatching
When using $watch, it's important to unwatch when the component is destroyed to prevent memory leaks. $watch returns an unwatch function.
<template>
<div>
<p>Value: {{ value }}</p>
<button @click="startWatching">Start Watching</button>
</div>
</template>
<script>
export default {
data() {
return {
value: 10
}
},
methods: {
startWatching() {
const unwatch = this.$watch('value', (newValue, oldValue) => {
console.log('Value changed (programmatically):', newValue, oldValue);
});
// Unwatch in the beforeDestroy lifecycle hook
this.$once('hook:beforeDestroy', unwatch);
}
}
}
</script>
Key Considerations:
- Performance: Deep watching can be expensive, especially for large objects. Use it only when necessary.
- Side Effects: Watchers are best suited for side effects. Avoid complex logic within watchers; consider using computed properties for data transformations.
- Memory Leaks: Always unwatch programmatically created watchers to prevent memory leaks. Use
this.$once('hook:beforeDestroy', unwatch)for a clean unwatch in thebeforeDestroylifecycle hook. - Alternatives: Consider using computed properties if you need to derive new data based on existing data. Computed properties are cached and only re-evaluated when their dependencies change, which can be more efficient than watchers in some cases.