On Vue.js documentation there is an example like below:
var vm = new Vue({
el: '#demo',
data: {
firstName: 'Foo',
lastName: 'Bar',
fullName: 'Foo Bar'
},
watch: {
firstName: function (val) {
this.fullName = val + ' ' + this.lastName
},
lastName: function (val) {
this.fullName = this.firstName + ' ' + val
}
}
})
The above code is imperative and repetitive. Compare it with a computed property version:
var vm = new Vue({
el: '#demo',
data: {
firstName: 'Foo',
lastName: 'Bar'
},
computed: {
fullName: function () {
return this.firstName + ' ' + this.lastName
}
}
})
What are the situations when watchers more suitable than computed properties? How should i decide which to choose? Documentation keeps saying it is more "generic" but does not really put its purpose.
Vue.js is reactive
That means it is able to react to things like user input and data changes. I recommend reading up on the reactivity system to have a better understanding of the mechanics Vue is using under the hood when a data change is observed. There are three main ways to have your components make use of Vue’s reactive nature. These are Methods, Computed Properties and Watchers. Without some scrutiny, these options may seem interchangeable (and in some ways they are) but each of them has their best use case scenario. To help illustrate the examples, I will make a small grading app that allows a teacher to input test scores for students in their class, view the average grade and set up the scaffolding for an auto-save feature.
METHODS
TL;DR – Use methods when you want to alter a component’s state or when an event has occurred that isn’t necessarily related to the instance data being mutated. Methods can take arguments but do not keep track of any dependencies. When you use a method, it usually creates some side effect within the component and methods are run each time the component is reloaded. This means if the UI is updated very frequently, this method (and any other methods on the component) will run as well. This could cause performance issues or lag in the UI.
Below is the beginning of our grading app. There is no validation or anything and its not pretty, I know. We have a small set of tests in our data object (student name and score). And a method we can use to add another test object to our data property ‘tests’.
COMPUTED PROPERTIES
TL;DR – Use a computed property when you want to mutate a property that is dependent upon another property being changed. Computed properties are typically dependent on other data properties. Any change to the dependent properties will trigger the logic for the computed property. Computed properties are cached based on their dependencies so they will only rerun if a dependency changes. (For example, a computed property that returns a new Date() will never rerun because the logic will never run more than 1 time) Computed properties are getters by default but a setter function can be set if needed to achieve similar functionality.
In our grading app, we want to keep track of the average test score as we enter more data. Let's add a computed property called ‘average’ that will return the average score of the tests in our dataset. The ‘average’ computed property will be updated any time we add another test score.
WATCHERS
TL;DR – Use watchers when you need to perform some logic as a result of a change that took place on a specific data property. Watched properties only act on one property. This is most useful when you want to perform asynchronous or expensive operations in response to changing data. Keep in mind watchers only change when that specific data property changes.
Let us pretend the end user of our little grading app is a professor with 300 tests to grade. That can take a long time. An auto-save feature would be nice to have in case our end user gets to the end of the pile of tests and forgets to manually hit save. In our code lets add a watcher to our previously created computed property ‘average’. Whenever it is changed (as a result of a new test score being added and the average being updated) let's call a new ‘autosave’ method that could be used to call an API and save our test scores.
Computed Properties
A computed property sample:
what does this particular piece of code do?
It creates a property named
val
for the component (on the prototype so<vueInstanece>.hasOwnProperty('val')
would showfalse
).It has a dependency tree which consists of reactive properties (data properties, other computed properties) in this case :
this.someDataProperty
, which means the moment the dependencies change, the computed property will be recalculated.Although debated, can't have arguments passed to it. So something like
can't be done
[EDIT] See: https://vuejs.org/v2/guide/computed.html#Computed-Setter
Watcher
A watcher sample:
It does not create any new property, but it watches the changes over a reactive property.
Watches only one specific property, unlike computed where any dependent property change can cause recalculation.
Has arguments of new and old value.
So computed properties would be the way to go if:
You want a property that depends on other properties always. Like text formatting for a template, which is even the example in your code.
Or reducing variable lengths as this is quite common:
can be reduced to:
Not just reduction in variable size, each time the store updates, you will have the latest value in the
someDeeplyNestedProperty
.And Watchers are useful if you want to see if one reactive property has changed to a favourable value to know that you're ready to perform an action.
like:
For the purpose of this example, computed properties are indeed better. In the example that utilizes watchers notice that this line of code:
this.fullName = this.firstName + ' ' + val
is very similar to this:
this.fullName = val + ' ' + this.lastName
Both serve the same purpose, they are watching for changes in the first or last name and update
fullName
accordingly. But since this will never change andfullName
will always be composed byfirstName
andlastName
then we can avoid the fuss and create a computed property. Then every timefirstName
andlastName
change,fullName
will be updated automatically.There are some cases where using watchers is better though. When you want to do some serious computation of write some async code then a watcher might be more suitable.
For example, if you had something like the following:
And you want, every time that
name
changes, to make an API call with it, get the result and process it, then a watcher is more appropriate:To do that with a computed property you would have to implement a
computed get()
and acomputed set()
property that would result in more code.Also notice that in the documentation's example we have a property,
fullName
that is composed a.k.a computed by two other properties. In my examplename
is not computed, in the literal sense of the term. We just want to observe it, so using a computed property would be more of a hack instead of a design pattern.watch
Use watch when you want to perform asynchronous or expensive operations in response to changing data.
computed
Use computed in other cases. The computed properties are cached based on their dependencies. Mostly used when you want to only re-evaluate some of its dependencies have changed.
You use a watcher when you want to mutate a value or perform an action based on some other value changing. A good example of this is when you set a value based on a prop and you want to react to any changes:
See this JSFiddle: https://jsfiddle.net/fjdjq7a8/
That example is a bit contrived and doesn't really work in the real world because we aren't syncing values, so here's a real example where I am using this in one of my open source projects:
Computeds are for arbitrarily manipulating the data itself, so things like concatenating strings and calculating values.
Computed properties have a a very specific purpose: composing new data derived from other data. They are used whenever you have some data and need to transform it, filter it, or otherwise manipulate it before using it in the template.
Computed properties always have to return a value, should not have any side effects, and they have to be synchronous.
So there are quite some situations where computed properties won't help you, for example: your component receives a prop, and whenever the prop changes, your component had to make an ajax request. For this, you would need a watcher.
Watchers are not useful as often as computed properties, so you should always think about whether or not a computed property can solve your problem, and only fall back on a watcher (or sometimes a method) if that is not the case.