Vue.js offers a mechanism that tracks changes in the application state. This feature is called watcher, — it lets you monitor changes to reactive properties and execute custom logic in response.
What is a Watcher?
A watcher in Vue.js is a function that observes a reactive property (e.g., ref
or reactive
) and triggers a callback whenever the property changes. Watchers are often used for tasks like:
- Logging state changes
- Triggering side effects (e.g., network requests)
- Synchronizing state between components or external systems
Implementing Watchers: Step-by-Step Guide
Let’s walk through an example to understand watchers better.
Basic Setup
First, create a Vue.js application and define a reactive property. On every button click it will log the message to the console because we change the value of the count.
<div id="app">
<div>Count: {{ count }}</div>
<button @click="count++">Click</button>
</div>
<script>
const { createApp, ref, watch } = Vue;
createApp({
setup() {
const count = ref(0); // Initialize a reactive property
watch(count, (newValue, oldValue) => {
console.log(`Count changed: New = ${newValue}, Old = ${oldValue}`);
});
return { count };
},
}).mount("#app");
</script>
Reactive Property (ref
): We initialize count
with ref(0)
Watcher: The watch
function monitors count
and logs its new and old values whenever it changes
Watching More Complex Data Structures
Reactive properties can also be objects. For example:
const count = ref({ number: 0, otherProp: "Hello" });
If we watch this object directly, we may encounter issues. For instance:
watch(count, (newValue, oldValue) => {
console.log(`New: ${newValue}, Old: ${oldValue}`);
});
The newValue and oldValue might not update as expected because Vue tracks objects differently. To fix this, watch specific properties using a getter function:
const count = ref({ number: 0, otherProp: "Hello" });
watch(() => count.value.number, (newValue, oldValue) => {
console.log(`Number changed: New = ${newValue}, Old = ${oldValue}`);
});
This ensures the watcher tracks the number property correctly.
Using reactive
instead of ref
For less verbosity, consider using reactive
:
const count = reactive({ number: 0, otherProp: "Hello" });
watch(() => [count.number, count.otherProp], ([newNum, newOther], [oldNum, oldOther]) => {
console.log(`Number: New = ${newNum}, Old = ${oldNum}`);
console.log(`OtherProp: New = ${newOther}, Old = ${oldOther}`);
});
With reactive
:
- No need to use
.value
- Properties are automatically unwrapped
Watching Multiple Properties
You can also watch multiple properties by passing an array to the watcher:
watch(
() => [count.number, count.otherProp],
([newNum, newOther], [oldNum, oldOther]) => {
console.log(`Number changed: New = ${newNum}, Old = ${oldNum}`);
console.log(`OtherProp changed: New = ${newOther}, Old = ${oldOther}`);
}
);
Here, both count.number
and count.otherProp
are monitored. The watcher triggers whenever either property changes..
When to Use Watchers?
Watchers are ideal for scenarios where you need to respond to changes in reactive data without directly binding the logic to the template. Some use cases include:
- Fetching data when a value changes (e.g., filtering a list)
- Debouncing or throttling input
- Monitoring specific state changes for analytics
Best Practices
- Avoid Overusing Watchers: If possible, use computed properties or
v-model
or simpler reactivity needs - Be Specific: Use getters to monitor specific parts of complex objects
- Use
Reactive
for Objects: : Prefer reactive
for simpler syntax with objects - Combine with Lifecycle Hooks: Integrate watchers with hooks like onMounted for initializing logic