Vue.js is an easy to use web app framework that we can use to develop interactive front end apps.
In this article, we’ll look at Vue.js computed properties and watchers.
Computed Properties
To make more template expressions more concise and reusable, we can create computed properties to compute results from existing properties as their value changes.
We can define computed properties in a Vue instance by putting them in the computed
property of the options object that we pass into the Vue
constructor.
For example, we can write the following in src/index.js
:
new Vue({
el: "#app",
data: {
message: "foo"
},
computed: {
spelledMessage() {
return this.message.split("").join("-");
}
}
});
Then we can use it like any other field in our HTML template. So in index.html
, we can write:
<!DOCTYPE html>
<html>
<head>
<title>Hello</title>
<meta charset="UTF-8" />
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head> <body>
<div id="app">
<p>{{message}}</p>
<p>{{spelledMessage}}</p>
</div>
<script src="./src/index.js"></script>
</body>
</html>
We should then get:
foof-o-o
on our screen.
The spelledMessage
method is a getter that’s used by Vue.js. It takes the returned value of it and then put it in place of {{spelledMessage}}
.
Therefore, the name of the placeholder in the template must be the same as the name of the method that returns the value that we want in the computed
object.
We can also get the value from the returned Vue instance as follows:
const vm = new Vue({
el: "#app",
data: {
message: "foo"
},
computed: {
spelledMessage() {
return this.message.split("").join("-");
}
}
});console.log(vm.spelledMessage);
We should see f-o-o
logged from the console.log
.
We can bind to computed properties in templates like any other property. Vue knows that vm.spelledMessage
depends on vm.message
, so the bindings will be updated when vm.message
is updated.
The computed getter method doesn’t commit side effects, which makes it easy to test and understand.
Computed Caching vs Methods
One good feature of computed properties is that the result of it is cached.
There’s no caching for results returned from methods.
So instead of using methods, we should use computed properties for cases where we’re computing something from existing properties.
A computed property is only re-evaluated once a reactive property is updated.
For example, if we have:
const vm = new Vue({
el: "#app",
data: {
message: "foo"
},
computed: {
now() {
return Date.now();
}
}
});
It’ll never update after its first computed since it doesn’t depend on any reactive property from the data
property.
Without caching computed properties keep getting computed as reactive properties that they depend on change, which means the app gets slow as reactive properties that the computed property depends on are changing.
Computed vs Watched Property
Watch properties are useful for watching for changes in a property as the name suggests.
However, because of the caching feature of computed properties, we should use them as much as we can.
For instance, we can simplify the following:
new Vue({
el: "#app",
data: {
name: "Joe",
age: 1,
person: undefined
},
watch: {
name: {
immediate: true,
handler(newVal) {
this.person = `${newVal} - ${this.age}`;
}
},
age: {
immediate: true,
handler(newVal) {
this.person = `${this.name} - ${newVal}`;
}
}
}
});
to:
new Vue({
el: "#app",
data: {
name: "Joe",
age: 1
},
computed: {
person() {
return `${this.name} - ${this.age}`;
}
}
});
As we can see, the first example was much more complex. We have to set immediate
to true
in each property so that it’ll watch the initial value change. Then we have to define the value change handler for each reactive property.
Then in each handler, we have to set the person
property so that we can combine name
and age
.
On the other hand, with computed properties, we only have one method which returns the fields combined in the way we want.
It’s much more convenient for us and the app that we create is also faster because of caching.
Computed Setter
We can also create a setter function for computed properties.
For example, we can write:
new Vue({
el: "#app",
data: {
name: "Joe",
age: 1
},
computed: {
person: {
get() {
return `${this.name} - ${this.age}`;
},
set(newValue) {
const [name, age] = newValue.split("-");
this.name = name.trim();
this.age = age.trim();
}
}
}
});
The getter function returns the same thing as before, but now we also have a setter function.
The setter function splits the newValue
, which has the computed value from the getter function, we split the result and set the result back to the corresponding reactive properties.
We can use a setter as follows. We can put the following in src/index.js
:
new Vue({
el: "#app",
data: {
firstName: "Joe",
lastName: "Smith"
},
computed: {
name: {
get() {
return `${this.firstName} ${this.lastName}`;
},
set(newValue) {
const [firstName, lastName] = newValue.split(" ");
this.firstName = (firstName || "").trim();
this.lastName = (lastName || "").trim();
}
}
}
});
And in index.html
, we put:
<!DOCTYPE html>
<html>
<head>
<title>Hello</title>
<meta charset="UTF-8" />
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head> <body>
<div id="app">
<p>{{name}}</p>
<input type="text" v-model="name" />
</div>
<script src="./src/index.js"></script>
</body>
</html>
Then when we change the value in the input, we get the new values assigned back to the firstName
and lastName
fields with the setter.
Conclusion
We can use computed properties to compute values from existing properties.
Returned values from computed properties are cached to reduce the amount of computation required.
Computed properties can also have setters, where we can do things with the new values returned from the computed property getter.