24.01.2026

Vue.js’te Performans İnce Ayarı: Reactivity, computed ve v-memo ile Akıcı Arayüzler

Vue reactivity maliyetlerini azaltma, computed/ watchEffect farkı ve v-memo ile render optimizasyonu.

Vue projeleri büyüdükçe çoğu problem “özellik eksikliği”nden değil, gereksiz yeniden render ve pahalı reactivity zincirlerinden çıkar. Bu yazıda, Vue 3 ile arayüzü daha akıcı hale getirecek pratik performans ayarlarına odaklanıyoruz.

1) Reactivity maliyeti nerede oluşur?

Vue, state değişimlerini izler ve template’i günceller. Sorun; çok sık değişen state’in (örn. arama input’u, slider, websocket akışı) geniş alanları etkilemesiyle ortaya çıkar.

İpucu: Sık değişen veriyi, UI’ın sadece gerekli kısmına “yakın” tutun. Büyük objeleri tek parça reactive yapmak yerine parçalayın.

import { ref, shallowRef } from 'vue'

const query = ref('')           // sık değişir
const results = shallowRef([])  // büyük dizi; derin izleme istemiyoruz

shallowRef / shallowReactive: Derin izleme yapmadığı için büyük veri yapılarında (liste, grafik datası, cache) işe yarar.

2) computed: “cache” gibi düşünün

Template içinde fonksiyon çağırmak kolaydır ama her render’da tekrar çalışabilir.

Kötü örnek:

<template>
  <li v-for="u in filteredUsers()" :key="u.id">{{ u.name }}</li>
</template>

İyi örnek:

import { computed, ref } from 'vue'

const users = ref([])
const query = ref('')

const filteredUsers = computed(() => {
  const q = query.value.toLowerCase().trim()
  if (!q) return users.value
  return users.value.filter(u => u.name.toLowerCase().includes(q))
})

<template>
  <li v-for="u in filteredUsers" :key="u.id">{{ u.name }}</li>
</template>

computed bağımlılıklar değişmedikçe sonucu cache’ler. Büyük listelerde ciddi fark yaratır.

3) watch vs watchEffect: doğru aracı seçin

  • watch: Hangi kaynağı izlediğin belli, yan etkiler için kontrollüdür.
  • watchEffect: İçeride kullanılan reaktifleri otomatik toplar; hızlı prototip için iyi ama “fazla tetiklenme” riski taşır.

Arama için debounce + watch:

import { ref, watch } from 'vue'

const query = ref('')
const loading = ref(false)

let t
watch(query, (q) => {
  clearTimeout(t)
  t = setTimeout(async () => {
    loading.value = true
    // await fetchResults(q)
    loading.value = false
  }, 250)
})

Bu sayede her tuş vuruşunda API çağırmazsın.

4) v-memo: render’ı “kilitleyin” (Vue 3.2+)

Bir liste öğesi sık değişmeyen verilere bağlıysa, v-memo ile Vue’ya “şunlar değişmedikçe tekrar render etme” diyebilirsin.

<template>
  <ul>
    <UserRow
      v-for="u in users"
      :key="u.id"
      :user="u"
      v-memo="[u.id, u.updatedAt]"
    />
  </ul>
</template>

updatedAt değişmedikçe satırın VDOM karşılaştırması bile minimuma iner.

5) Büyük listeler: “virtualization” düşünün

10.000 satırı DOM’a basmak yerine sadece görünen kısmı render edin. Vue ekosisteminde vue-virtual-scroller gibi çözümler var. Mantık basit: görünür alan + buffer.

Minimal yaklaşım:

  • Satır yüksekliği sabitse virtualization çok kolay.
  • Değişkense ölçüm maliyeti artar; yine de çoğu zaman kazançlıdır.

6) Küçük ama etkili ipuçları

  • Template’te pahalı hesaplardan kaçın, computed kullan.
  • Büyük immutable verileri markRaw ile reactivity dışına al (örn. 3rd party instance’lar).
  • Bileşenleri gereksizce “prop zinciri” ile güncelleme; gerçekten gerekli prop’ları geçir.
  • Chrome Performance + Vue Devtools ile “hangi component kaç kere render olmuş” kontrol et.

Sonuç

Vue zaten hızlı; ama akıcılık, neye reactivity verdiğin ve render’ı nasıl sınırladığın ile belirleniyor. computed ile cache, watch ile kontrollü yan etki, shallowRef ile derin izlemeyi azaltma ve v-memo ile render kilidi; en hızlı kazanım sağlayan dört hamle.