Introduction
Recently, I started learning VueJS, and this article is part of the series of my notes while learning it. So far, I mostly used data and methods properties of the component. However, there are other, better ways to expose data or run some function and that is the topic of this post. So let us start with creating the initial component first.
Initial component
For better demonstration, I am starting with the parent component. This component has two counters and two buttons, each incrementing one of them. Then this component passes it to the child component which will display counter values in different ways. While simple and redundant, this example will help demonstrate the different ways and impacts of each one.
<template>
<div>
<div>{{firstCounter}}</div>
<div>{{secondCounter}}</div>
<ChildComponent :firstCounter="firstCounter" :secondCounter="secondCounter" />
<div>
<button @click="updateFirstCounter()">increment first</button>
<button @click="updateSecondCounter()">increment second</button>
</div>
</div>
</template>
<script>
import ChildComponent from './ChildComponent.vue';
export default {
components: {ChildComponent},
data() {
return {
firstCounter: 0,
secondCounter: 0,
}
},
methods: {
updateFirstCounter() {
this.firstCounter++;
},
updateSecondCounter() {
this.secondCounter++;
}
}
}
</script>
Data object
You could display data in the template by using the props directly, but for now, let’s ignore that part. So we can use another, very simple way to do it by returning props data as a return value of the data object.
<template>
<div>
<div>
<div>data</div>
<div>first: {{first}}</div>
<div>second: {{second}}</div>
</div>
</div>
</template>
While very simple, this kind of display does come with its very big problem. If you run this code, it will display both values to be zero. But if you click any of the buttons to trigger the increment, the value will not change. The reason is that the data object is created on the mounting of the component, and doesn’t re-run on the props update.
Methods
One way to overcome the previous problem is by using the methods. In the methods, you can return a prop value, and every time it changes, the method will run, and an updated value is displayed.
<template>
<div>
<div>
<div>data</div>
<div>first: {{first}}</div>
<div>second: {{second}}</div>
</div>
<div>
<div>method</div>
<div>first: {{firstMethod()}}</div>
<div>second: {{secondMethod()}}</div>
</div>
<div>
<div>computed</div>
<div>first: {{firstComputedMethod}}</div>
<div>second: {{secondComputedMethod}}</div>
</div>
<div>
<div>Watch</div>
<div>first: {{firstWatch}}</div>
<div>second: {{secondWatch}}</div>
</div>
</div>
</template>
<script>
export default {
props: ["firstCounter", "secondCounter"],
data() {
const {firstCounter, secondCounter} = this.$props;
return {
first: firstCounter,
second: secondCounter,
}
},
methods: {
firstMethod() {
console.log("firstMethod")
return this.$props.firstCounter
},
secondMethod() {
console.log("secondMethod")
return this.$props.secondCounter
}
},
}
</script>
The code above does resolve the previous issue. But it also comes with its problems. And that is performance. Maybe you noticed I placed logs in both functions. If you run this code, and press one of the incrementing buttons, you can notice the log message from both functions. That is because they are run on every update, so if any data value or prop changes it will run all of the used methods. If you have a lot of them, it could cause performance problems.
Computed
Computed methods are quite similar to the normal methods, except for the previous, regular method issue. When running them, Vue checks what values each computed method uses, and only runs it when one of those values changes.
<template>
<div>
<div>
<div>computed</div>
<div>first: {{firstComputedMethod}}</div>
<div>second: {{secondComputedMethod}}</div>
</div>
</div>
</template>
<script>
export default {
props: ["firstCounter", "secondCounter"],
data() {
return {
}
},
computed: {
firstComputedMethod() {
console.log("firstComputedMethod")
return this.$props.firstCounter
},
secondComputedMethod() {
console.log("secondComputedMethod")
return this.$props.secondCounter
}
},
}
</script>
If you check the logs, when updating one counter, only one method is run, not all of them.
Watchers
Watchers are quite similar to the computed. Once a dependency changes, it will run some functions. Then in that function, you can do some work or just update the value in the data. Which would result in it being updated with the new prop value and displayed. The difference is that you need to name the watcher by the prop or data name. That is the way it knows which value to watch for changes.
<template>
<div>
<div>
<div>Watch</div>
<div>first: {{firstWatch}}</div>
<div>second: {{secondWatch}}</div>
</div>
</div>
</template>
<script>
export default {
props: ["firstCounter", "secondCounter"],
data() {
const {firstCounter, secondCounter} = this.$props;
return {
firstWatch: firstCounter,
secondWatch: secondCounter,
}
},
watch: {
firstCounter () {
console.log("first counter watch")
this.firstWatch = this.$props.firstCounter
},
secondCounter () {
console.log("second counter watch")
this.secondWatch = this.$props.secondCounter
}
}
}
</script>
Computed vs Watch
At this moment you might be thinking what is the difference between the computed and watchers, and when you should watch which? Mostly, you should use computed. The reason is that computed can run depending on multiple values, and you can name them appropriately and not only based on the props or data. However, if you are running some asynchronous operation, watchers could be more suitable.
The code used in this article can be found in my GitHub repository.
For more, you can follow me on Twitter, LinkedIn, GitHub, or Instagram.
Top comments (0)