Vue Front-end Инженер

Vue Front-end Инженер

Роадмап навыков для прокачки

Вычисляемые свойства computed

VueJSReactivity Systemcomputed

Основная идея

computed — функция для создания вычисляемых свойств во Vue. Она автоматически вычисляет значение на основе зависимостей и кэширует результат. Пересчёт происходит только при изменении зависимых данных.

Ключевые аспекты

  • Кэширование — результат вычисляется один раз и кэшируется до изменения зависимостей
  • Автоотслеживание — Vue автоматически определяет, от каких данных зависит computed
  • Ленивость — вычисление происходит только при обращении к значению
  • Только чтение — по умолчанию computed доступен только для чтения
  • Синтаксис — доступ через .value в скрипте, автоматически в шаблоне

Плюсы

  • Производительность — не пересчитывается при каждом рендере
  • Декларативность — описываем что вычислить, а не когда
  • Автоматическая синхронизация с зависимостями
  • Простой синтаксис

Минусы

  • Нельзя передавать аргументы (в отличие от методов)
  • По умолчанию только для чтения
  • Нужен .value в скрипте (как ref)

Частые ошибки на собеседованиях

  • Не понимают разницу между computed и методом (кэширование vs пересчёт при каждом вызове)
  • Путают computed с watch (computed — производное значение, watch — побочные эффекты)
  • Забывают про .value при использовании в скрипте
  • Не знают, что computed может иметь setter

Введение и проблематика

В приложениях часто нужно вычислять производные данные:

  • Полное имя из имени и фамилии
  • Отфильтрованный список товаров
  • Итоговая сумма корзины

Можно использовать методы, но они пересчитываются при каждом рендере. Computed свойства решают эту проблему через кэширование.

computed — это реактивное кэшируемое значение. Оно пересчитывается только когда изменяются его зависимости.


Базовая теория

Создание computed

vue
<template>
  <p>Имя: {{ firstName }}</p>
  <p>Фамилия: {{ lastName }}</p>
  <p>Полное имя: {{ fullName }}</p>
</template>
 
<script setup>
import { ref, computed } from 'vue'
 
const firstName = ref('Иван')
const lastName = ref('Иванов')
 
// Вычисляемое свойство
const fullName = computed(() => {
  return `${firstName.value} ${lastName.value}`
})
</script>

Доступ к значению

vue
<script setup>
import { ref, computed } from 'vue'
 
const count = ref(0)
const doubled = computed(() => count.value * 2)
 
// В скрипте — через .value
console.log(doubled.value) // 0
 
count.value = 5
console.log(doubled.value) // 10
</script>
 
<template>
  <!-- В шаблоне — автоматически -->
  <p>{{ doubled }}</p>
</template>

Практические примеры

Фильтрация списка

vue
<template>
  <input v-model="searchQuery" placeholder="Поиск..." />
  <ul>
    <li v-for="user in filteredUsers" :key="user.id">
      {{ user.name }}
    </li>
  </ul>
</template>
 
<script setup>
import { ref, computed } from 'vue'
 
const searchQuery = ref('')
const users = ref([
  { id: 1, name: 'Иван Иванов' },
  { id: 2, name: 'Пётр Петров' },
  { id: 3, name: 'Мария Сидорова' }
])
 
const filteredUsers = computed(() => {
  const query = searchQuery.value.toLowerCase()
  return users.value.filter(user =>
    user.name.toLowerCase().includes(query)
  )
})
</script>

Сумма корзины

vue
<template>
  <div v-for="item in cart" :key="item.id">
    {{ item.name }} — {{ item.price }} × {{ item.quantity }}
  </div>
  <p><strong>Итого: {{ totalPrice }} ₽</strong></p>
</template>
 
<script setup>
import { ref, computed } from 'vue'
 
const cart = ref([
  { id: 1, name: 'Товар A', price: 100, quantity: 2 },
  { id: 2, name: 'Товар B', price: 250, quantity: 1 }
])
 
const totalPrice = computed(() => {
  return cart.value.reduce((sum, item) => {
    return sum + item.price * item.quantity
  }, 0)
})
</script>

computed vs методы

vue
<template>
  <p>{{ reversedMessage }}</p>
  <p>{{ reversedMessage }}</p>
  <p>{{ reversedMessage }}</p>
</template>
 
<script setup>
import { ref, computed } from 'vue'
 
const message = ref('Привет')
 
// Вычисляется ОДИН раз, результат кэшируется
const reversedMessage = computed(() => {
  console.log('computed вызван')
  return message.value.split('').reverse().join('')
})
</script>

В консоли: computed вызван — один раз


computed с геттером и сеттером

По умолчанию computed только для чтения. Но можно добавить сеттер:

vue
<template>
  <input v-model="fullName" />
  <p>Имя: {{ firstName }}</p>
  <p>Фамилия: {{ lastName }}</p>
</template>
 
<script setup>
import { ref, computed } from 'vue'
 
const firstName = ref('Иван')
const lastName = ref('Иванов')
 
const fullName = computed({
  get() {
    return `${firstName.value} ${lastName.value}`
  },
  set(newValue) {
    const parts = newValue.split(' ')
    firstName.value = parts[0] || ''
    lastName.value = parts[1] || ''
  }
})
</script>

Пограничные кейсы

⚠️

computed должен быть чистой функцией — без побочных эффектов. Не изменяйте состояние внутри computed.

vue
<script setup>
import { ref, computed } from 'vue'
 
const count = ref(0)
const log = ref([])
 
// ❌ Неправильно — побочный эффект
const doubled = computed(() => {
  log.value.push('вычислено') // Изменяем состояние!
  return count.value * 2
})
 
// ✅ Правильно — чистая функция
const doubledCorrect = computed(() => {
  return count.value * 2
})
</script>
🚫

Не используйте асинхронные операции внутри computed. Для этого есть watch или watchEffect.

vue
<script setup>
import { ref, computed, watch } from 'vue'
 
const userId = ref(1)
 
// ❌ Неправильно — асинхронный код в computed
const user = computed(async () => {
  return await fetchUser(userId.value)
})
 
// ✅ Правильно — используйте watch
const user = ref(null)
watch(userId, async (newId) => {
  user.value = await fetchUser(newId)
}, { immediate: true })
</script>

Плюсы и минусы

Аспектcomputedметод
КэшированиеДаНет
Вызов в шаблоне{{ value }}{{ method() }}
АргументыНетДа
Для производных данныхИдеальноИзбыточно
Для действийНе подходитИдеально

Когда использовать

computed:

  • Производные данные (fullName из firstName + lastName)
  • Фильтрация и сортировка списков
  • Форматирование значений
  • Любые синхронные вычисления на основе реактивных данных

методы:

  • Обработчики событий
  • Операции с аргументами
  • Асинхронные операции
  • Действия с побочными эффектами

Вопросы интервьюера

Q: В чём разница между computed и методом?

computed кэшируется и пересчитывается только при изменении зависимостей. Метод вызывается каждый раз при обращении.

Q: Когда пересчитывается computed?

Только когда изменяются реактивные зависимости, использованные внутри функции computed.

Q: Можно ли записать значение в computed?

По умолчанию нет. Но можно создать computed с getter и setter для двусторонней привязки.

Q: Чем computed отличается от watch?

computed возвращает вычисленное значение. watch выполняет побочные эффекты при изменении данных.


Источники