We can perform complex calculations & logic within templates, thanks to reactive data, and still have the UI automatically updated. You may find that having the complex expression inside the template doesn't really look nice inside the template:
<!-- I know this isn't a complex expression, but I'm giving an example of what I mean by expression inside a template -->
<template>
<p>Are books available: {{ category.books.length > 0 ? 'Yes' : 'No' }}</p>
</template>
So, we throw the expression inside a function/method and problem solved:
<script setup>
import { reactive } from "vue";
const category = reactive({
books: ["Clean Code", "The Pragmatic Programmer", "Design Patterns"],
});
const doBooksExist = () => {
return category.books.length > 0 ? "Yes" : "No";
};
</script>
<template>
<p>Are books available: {{ doBooksExist() }}</p>
</template>
Again, I know that the expression inside the function in the example above isn't a complex one but if it were a complex one, it means that every time the UI is rendered the complex calculation is performed even if the reactive data values do not change. Doing the complex calculations & logic every time is costly, especially when you have a big web app.
This is where the computed property comes in. A computed property is a value that depends on another value, uses the value which it depends on to perform calculations and allows us to easily reuse the result of the computation. Computed properties keep track of changes in the values they depend on. Therefore, if the values don't change they use the cached result from the last time they performed the calculation instead of doing the calculation again.
I would say the benefits of a Computed Property are that they cache the result and that they remove the need to have complex expressions inside our templates, leaving our code looking clean. If you need to perform complex calculations/logic that include reactive data, it is recommended to use computed properties.
Below is an example of how you would use Computed Properties in <script setup>
:
<script setup>
import { reactive, computed } from "vue";
const category = reactive({
books: ["Clean Code", "The Pragmatic Programmer", "Design Patterns"],
});
const doBooksExist = computed(() => {
return category.books.length > 0 ? "Yes" : "No";
});
</script>
<template>
<p>Are books available: {{ doBooksExist }}</p>
</template>
Using <script>
:
<template>
<p>Are books available: {{ doBooksExist }}</p>
</template>
<script>
import { reactive, computed } from "vue";
export default {
setup() {
const category = reactive({
books: ["Clean Code", "The Pragmatic Programmer", "Design Patterns"],
});
const doBooksExist = computed(() => {
return category.books.length > 0 ? "Yes" : "No";
});
return { category, doBooksExist };
},
};
</script>
<template>
<p>Are books available: {{ doBooksExist }}</p>
</template>
<script>
export default {
data() {
return {
category: {
books: ["Clean Code", "The Pragmatic Programmer", "Design Patterns"],
},
};
},
computed: {
doBooksExist() {
return this.category.books.length > 0 ? "Yes" : "No";
},
},
};
</script>
From the Vue Documentation, Computed Properties are getter-only by default. If you need to have a "writable" computed property which the documentation says is a rare case, you can provide both a getter & setter:
In Composition API:
const firstName = ref("John");
const lastName = ref("Doe");
const fullName = computed({
get() {
return firstName.value + " " + lastName.value;
},
set(newValue) {
[firstName.value, lastName.value] = newValue.split(" ");
},
});
function changeFullName() {
fullName.value = "James Bond";
}
In Options API (the code snippet below is from the Vue Documentation):
export default {
data() {
return {
firstName: "John",
lastName: "Doe",
};
},
computed: {
fullName: {
// getter
get() {
return this.firstName + " " + this.lastName;
},
// setter
set(newValue) {
// Note: we are using destructuring assignment syntax here.
[this.firstName, this.lastName] = newValue.split(" ");
},
},
},
};
I hope this article has helped you, you can let me know in the comments.