VueJS - Computed Properties
VueJS - Computed Properties
Section titled “VueJS - Computed Properties”Templates are great for simple expressions, but putting too much complex logic directly in your templates can make them cluttered and hard to maintain. For any complex logic involving reactive data, you should use computed properties.
Computed properties allow you to define a property that derives its value from other reactive state. They are cached based on their reactive dependencies, meaning they only re-evaluate when one of those dependencies changes.
Basic Example
Section titled “Basic Example”Imagine you have an object with nested reactive data, and you want to display a message based on whether it has items.
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>Vue 3 Computed Property Example</title> <script src="https://unpkg.com/vue@3"></script></head><body> <div id="app"> <h2>Author: {{ author.name }}</h2>
<!-- Directly in template (can get complex) --> <p>Has published books? {{ author.books.length > 0 ? 'Yes' : 'No' }}</p>
<!-- Using computed property (cleaner) --> <p>Has published books (Computed)? {{ publishedBooksMessage }}</p>
<h3>Books:</h3> <ul> <li v-for="(book, index) in author.books" :key="index">{{ book }}</li> </ul> <button @click="addBook">Add Book</button> <button @click="removeBook">Remove Book</button> </div>
<script> const { createApp, reactive, computed } = Vue;
createApp({ setup() { const author = reactive({ name: 'Jane Doe', books: ['Vue 2 - Advanced Guide', 'Vue 3 - Basic Guide'] });
// Define a computed property const publishedBooksMessage = computed(() => { console.log('Computed property executing...'); // Log to see when it runs // It depends on author.books return author.books.length > 0 ? 'Yes' : 'No'; });
function addBook() { author.books.push(`New Book #${author.books.length + 1}`); } function removeBook() { if (author.books.length > 0) { author.books.pop(); } }
return { author, publishedBooksMessage, // Expose computed property to template addBook, removeBook }; } }).mount('#app'); </script></body></html>In this example:
- We have a reactive
authorobject. - The first paragraph calculates the message directly in the template. This works, but if the logic were more complex, it would become unreadable.
- The
publishedBooksMessageis a computed property defined usingVue.computed(). It takes a getter function. - The computed property depends on
author.books.length. - In the template, we access
publishedBooksMessagelike a regular property (no()needed). - Open your browser console. Notice that “Computed property executing…” logs only when you add or remove a book (i.e., when
author.bookschanges), not every time the component re-renders. This demonstrates the caching behavior.
Computed Caching vs. Methods
Section titled “Computed Caching vs. Methods”You might wonder why we need computed properties when we could achieve the same result with a method.
// Using a method instead// setup() {// ...// function calculateBooksMessage() {// console.log('Method executing...');// return author.books.length > 0 ? 'Yes' : 'No';// }// return { author, calculateBooksMessage };// }// Template: <p>Has published books (Method)? {{ calculateBooksMessage() }}</p>While methods can produce the same result, the key difference lies in caching. Computed properties are cached based on their reactive dependencies. A computed property will only re-evaluate when one of its dependencies has changed. In contrast, a method call will always run the function whenever a re-render happens.
When to use which?
- Use computed properties when you need to derive state based on other reactive state, especially if the calculation is expensive or you want to benefit from caching.
- Use methods when you don’t need caching, when you need to perform actions in response to events (like in
@click), or when you need to pass arguments to the function.
Example: Caching Demonstration
Section titled “Example: Caching Demonstration”<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>Vue 3 Computed vs Method Caching</title> <script src="https://unpkg.com/vue@3"></script></head><body> <div id="app"> <p>Computed Value: {{ computedValue }}</p> <p>Computed Value: {{ computedValue }}</p> <!-- Accessing multiple times -->
<p>Method Value: {{ methodValue() }}</p> <p>Method Value: {{ methodValue() }}</p> <!-- Calling multiple times -->
<button @click="triggerReRender">Trigger Re-render (Change unrelated state)</button> <p>Unrelated Value: {{ unrelatedValue }}</p> </div>
<script> const { createApp, ref, computed } = Vue;
createApp({ setup() { const unrelatedValue = ref(0);
const computedValue = computed(() => { console.log('COMPUTED function running'); return Date.now(); // Example of a value that changes if re-run });
function methodValue() { console.log('METHOD function running'); return Date.now(); }
function triggerReRender() { unrelatedValue.value++; }
return { computedValue, methodValue, triggerReRender, unrelatedValue }; } }).mount('#app'); </script></body></html>Run this example and open the console:
- On initial load, both ‘COMPUTED function running’ and ‘METHOD function running’ will log (likely twice for the method due to multiple template calls).
- Notice the timestamp displayed for the computed property is the same in both places, while the method’s timestamp might differ slightly.
- Click the ‘Trigger Re-render’ button. This changes
unrelatedValue, causing a re-render. - Observe the console: ‘METHOD function running’ logs again (twice), but ‘COMPUTED function running’ does not log. The computed property returns its cached value because its dependencies haven’t changed.
- The displayed timestamp for the computed property remains the same, while the method’s displayed timestamp updates.
Writable Computed Properties (Getters and Setters)
Section titled “Writable Computed Properties (Getters and Setters)”By default, computed properties are getter-only. If you try to assign a new value to a computed property, you’ll get a runtime warning. However, you can create a writable computed property by providing both a get and a set function.
Example: Computed FullName with Setter
Section titled “Example: Computed FullName with Setter”<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>Vue 3 Writable Computed</title> <script src="https://unpkg.com/vue@3"></script></head><body> <div id="app"> <div>First Name: <input v-model="firstName"></div> <div>Last Name: <input v-model="lastName"></div> <hr> <div>Full Name (Computed): <input v-model="fullName"></div> </div>
<script> const { createApp, ref, computed } = Vue;
createApp({ setup() { const firstName = ref('John'); const lastName = ref('Doe');
// Writable computed property const fullName = computed({ // Getter: calculates value from source state get() { return firstName.value + ' ' + lastName.value; }, // Setter: called when fullName is assigned a new value set(newValue) { console.log('Setter called with:', newValue); // Update the source state based on the new value const names = newValue.split(' '); firstName.value = names[0] || ''; // Handle potential empty input lastName.value = names.slice(1).join(' ') || ''; // Handle names with multiple parts } });
return { firstName, lastName, fullName }; } }).mount('#app'); </script></body></html>In this example:
- The
fullNamecomputed property has bothgetandset. - The
getfunction combinesfirstNameandlastName. - The
setfunction is triggered when you type into the ‘Full Name’ input field (becausev-modeltries to setfullName). - The setter splits the input value and updates the original
firstNameandlastNamerefs. - This creates a two-way binding effect: changing the first/last name updates the full name, and changing the full name updates the first/last name fields.
Computed properties are a powerful feature for creating clean, maintainable templates by encapsulating derived state logic within your component’s script.
Further Reading: Vue’s official documentation on Computed Properties: https://vuejs.org/guide/essentials/computed.html