Watch vs Computed in Vue
Vue's watch()
function can serve a similar purpose
as computed properties. For example, suppose you want
to track the number of items in a user's shopping cart. You could represent
numItems
as a computed property:
const app = new Vue({
data: () => ({ items: [{ id: 1, price: 10, quantity: 2 }] }),
computed: {
numItems: function numItems() {
return this.items.reduce((sum, item) => sum + item.quantity, 0);
}
},
template: `<div>numItems is {{numItems}}</div>`
});
You could also define a watcher that updates a numItems
property every
time items
changes:
const app = new Vue({
data: () => ({
items: [{ id: 1, price: 10, quantity: 2 }],
numItems: 2
}),
watch: {
items: function updateNumItems() {
this.numItems = this.items.reduce((sum, item) => sum + item.quantity, 0);
}
},
template: `<div>numItems is {{numItems}}</div>`
});
Which approach should you use? In general, you should use computed properties
for updating values. You should only use watchers for "side effects" like
console.log()
, or HTTP requests. Here's why.
Keeping Updates in Sync
The problem with numItems
as a watcher is that you can accidentally update
numItems
without updating items
. That means numItems
may be out of sync.
const app = new Vue({
data: () => ({
items: [{ id: 1, price: 10, quantity: 2 }],
numItems: 2
}),
watch: {
items: function updateNumItems() {
this.numItems = this.items.reduce((sum, item) => sum + item.quantity, 0);
}
},
methods: {
myMethod: function() {
// Perfectly valid, since `numItems` is a data property.
this.numItems = 5;
}
},
template: `<div>numItems is {{numItems}}</div>`
});
On the other hand, if you try to update a computed property, Vue will treat it as a no-op and print the below warning:
[Vue warn]: Computed property "numItems" was assigned to but it has no setter.
So numItems
is guaranteed to stay in sync with the contents of items
, even
if you accidentally try to overwrite it.
When to Use Watchers
The Vue docs recommend using watchers when you want to perform async operations in response to changing data. For example, if you want to automatically save the cart
every time it changes, you might
do something like this:
const app = new Vue({
data: () => ({
items: [{ id: 1, price: 10, quantity: 2 }],
}),
watch: {
items: async function saveCart() {
await axios.put('/cart', items);
}
},
template: ...
});