Hack Frontend Community

Computed, Methods and Watchers in Vue.js

Introduction

Vue has three ways to work with reactive data: computed, methods, and watchers. Each has its purpose and characteristics.


Computed Properties

Computed properties are calculated based on other reactive data and are cached.

Composition API

<template>
  <p>First Name: {{ firstName }}</p>
  <p>Last Name: {{ lastName }}</p>
  <p>Full Name: {{ fullName }}</p>
</template>

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

const firstName = ref('John')
const lastName = ref('Doe')

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

Key Features

Caching

Result is calculated only when dependencies change:

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

const count = ref(0)

const doubleCount = computed(() => {
  console.log('Computing...') // Called only when count changes
  return count.value * 2
})
</script>

<template>
  <p>{{ doubleCount }}</p>
  <p>{{ doubleCount }}</p>
  <p>{{ doubleCount }}</p>
  <!-- console.log called only once -->
</template>

Writable Computed

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

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(' ')
  }
})

// Usage
fullName.value = 'Jane Smith'
// firstName.value = 'Jane'
// lastName.value = 'Smith'
</script>

Methods

Methods are functions that execute every time they're called.

<template>
  <p>{{ getFullName() }}</p>
  <button @click="greet">Greet</button>
</template>

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

const firstName = ref('John')
const lastName = ref('Doe')

function getFullName() {
  console.log('Method called')
  return `${firstName.value} ${lastName.value}`
}

function greet() {
  alert(`Hello, ${getFullName()}!`)
}
</script>

Key Features

No Caching

<template>
  <p>{{ getFullName() }}</p>
  <p>{{ getFullName() }}</p>
  <p>{{ getFullName() }}</p>
  <!-- console.log called 3 times -->
</template>

Accept Arguments

<template>
  <p>{{ formatPrice(100) }}</p>
  <p>{{ formatPrice(250, 'USD') }}</p>
</template>

<script setup>
function formatPrice(price, currency = 'USD') {
  return `${price} ${currency}`
}
</script>

Watchers

Watchers are functions that react to data changes and perform side effects.

<template>
  <input v-model="searchQuery" placeholder="Search..." />
</template>

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

const searchQuery = ref('')

watch(searchQuery, (newValue, oldValue) => {
  console.log(`Changed from "${oldValue}" to "${newValue}"`)
  // Perform API request
  fetchResults(newValue)
})

function fetchResults(query) {
  // API request
}
</script>

Key Features

Watch Multiple Sources

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

const firstName = ref('')
const lastName = ref('')

watch([firstName, lastName], ([newFirst, newLast]) => {
  console.log(`Full name: ${newFirst} ${newLast}`)
})
</script>

Immediate Execution

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

const count = ref(0)

watch(count, (newValue) => {
  console.log('Count changed:', newValue)
}, { immediate: true })
// Called immediately on creation
</script>

Deep Watching

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

const user = reactive({
  name: 'John',
  address: {
    city: 'New York'
  }
})

watch(user, (newValue) => {
  console.log('User changed:', newValue)
}, { deep: true })

// Triggers even when user.address.city changes
user.address.city = 'Los Angeles'
</script>

Comparison

Computed vs Methods

PropertyComputedMethods
CachingYesNo
ReactivityAutomaticManual
ArgumentsNoYes
Usage in template{{ computed }}{{ method() }}
Side effectsNot recommendedAllowed

When to use computed:

  • Calculate based on other data
  • Need caching
  • Synchronous operations

When to use methods:

  • Event handlers
  • Need parameters
  • Side effects (API requests, data changes)

Computed vs Watchers

PropertyComputedWatchers
PurposeCalculate valueSide effects
ResultReturns valueNo return
DependenciesAutomaticExplicit
AsyncNoYes

Common Mistakes

Using methods instead of computed

<!-- Inefficient -->
<template>
  <p>{{ getFullName() }}</p>
  <p>{{ getFullName() }}</p>
  <!-- Called twice -->
</template>

<!-- Efficient -->
<template>
  <p>{{ fullName }}</p>
  <p>{{ fullName }}</p>
  <!-- Calculated once -->
</template>

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

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

Side effects in computed

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

const count = ref(0)
const doubled = ref(0)

const bad = computed(() => {
  doubled.value = count.value * 2 // Side effect!
  return count.value * 2
})
</script>

<!-- Correct - use watcher -->
<script setup>
import { watch, ref } from 'vue'

const count = ref(0)
const doubled = ref(0)

watch(count, (newCount) => {
  doubled.value = newCount * 2
})
</script>

Conclusion

Computed:

  • Calculate values based on other data
  • Automatic caching
  • Synchronous operations

Methods:

  • Event handlers
  • Functions with parameters
  • Side effects

Watchers:

  • React to changes
  • Asynchronous operations
  • Side effects (API, localStorage)

In interviews:

Important to be able to:

  • Explain the difference between computed, methods and watchers
  • Describe caching in computed
  • Give examples of when to use each
  • Explain performance issues with wrong choice