Computed Properties or Watchers?

vue.js logo

Overview

When working with Vue.js, you will often need to react to data changes. Vue provides two main solutions for this:

  • Computed properties
  • Watchers

While they may seem similar at first, they have different purposes and uses.

Overview of differences

Computed properties

  • Purpose: Create new values from existing reactive data.
  • Return value: Will always return a value.
  • Caching: Cached automatically based on dependencies.
  • Template access: Accessible in templates
  • Side effects: Does not produce side effects and will ony re-evaluate when a reactive dependency is updated.

Watchers

  • Purpose: Performs side effects when data changes, such as updating the DOM or reactive state.
  • Return value: No return values.
  • Caching: Not automatically cached.
  • Template access: Not directly accessible in the template.
  • Side effects: Designed for side effects such as updating the DOM or state.

When to use computed properties

Computed properties are useful for:

  1. Calculating values from existing reactive data.
  2. Caching values to performance.
  3. Watching multiple reactive sources.
  4. Accessing results directly in templates.
  5. Extracting JavaScript expressions out of the template syntax ({{ }}).

Example: full name computation

<script setup>
import { ref, computed } from 'vue'

const first = ref('Homer')
const last = ref('Simpson')

const fullName = computed(() => {
  return `${first.value} ${last.value}`
})
</script>

<template>
  <p>Welcome, {{ fullName }}!</p>
</template>

When to use watchers

Watchers are great for:

  1. Performing side effects (API calls, working with the DOM, etc.)
  2. Asynchronous operations.
  3. Reacting to data changes.
  4. Updating external state.

Example: Fetching data from an API

<script setup>
import { ref, watch } from 'vue'

const searchQuery = ref('')
const searchResults = ref([])

watch(searchQuery, (newQuery, oldQuery) => {
  if (newQuery !== oldQuery) {
    getSearchResults(newQuery)
  }
})

const getSearchResults = async (query) => {
  // make api call and update searchResults.value
}
</script>

Performance considerations

Computed properties

  • Cached by default: Only re-calculates when reactive dependencies change.
  • Efficient: Perfect for expensive calculations.
  • Reactive: Auto updates when a dependency is updated.

Watchers

  • Not cached: Runs every time the watched value changes.
  • More control: Custom caching logic can be implemented if required.
  • Immediate execution: Runs immediately when reactive data is updated.

Common mistakes

Don't use computed properties for:

  • Manipulating the DOM.
  • Side effects.
  • Async tasks.

Don't use watchers for:

  • When you need the result in the template.
  • calculating values to render in the template.
  • Simple value calculations.

Summary

UsageComputed PropertiesWatchers
PurposeCalculate valuesPerform side effects
Multiple SourcesYesNeeds multiple watchers
CachingAutomaticManual (if required)
Return ValueAlways returns a valueNo return value
Template AccessYesNo
Async OperationsNoYes

Shopping cart example

This example simulates a shopping cart, using both computed and watch:

<script setup>
import { ref, computed, watch } from 'vue'

const items = ref([
    { name: "apple", price: 1.2 },
    { name: "banana", price: 2.3 },
]);
const taxRate = ref(0.2);

// Computed - used to calculate values
const subtotal = computed(() => {
    return items.value.reduce((sum, item) => sum + item.price, 0);
});

const tax = computed(() => {
    return subtotal.value * taxRate.value;
});

const total = computed(() => {
    return subtotal.value + tax.value;
});

function addItem(item) {
    items.value.push(item);
}

// Watcher - used to save to localStorage
watch(
    items,
    (newItems) => {
        localStorage.setItem("basket", JSON.stringify(newItems));
    },
    { deep: true }
);
</script>

And add an item in the template:

<button @click="addItem({ name: 'pear', price: 3.4 })">Add Item</button>

This example shows computed properties for calculating values (subtotal, tax, total) and a watcher for saving to the browsers localStorage (side effect).