Vue.js Performans Optimizasyonu: Reaktivite ve Render

## Vue.js Optimizasyonuna Genel Bakış Vue.js uygulamalarında performans sorunları genellikle üç kaynaktan beslenir: gereksiz yeniden render, aşırı bü...

Vue.js Performans Optimizasyonu: Reaktivite ve Render

Vue.js Optimizasyonuna Genel Bakış

Vue.js uygulamalarında performans sorunları genellikle üç kaynaktan beslenir: gereksiz yeniden render, aşırı büyüyen bundle ve reaktivite sisteminin yanlış kullanımı. Bu rehber, bu üç alana odaklanır ve orta seviye bir Vue geliştiricisinin günlük iş akışında uygulayabileceği adımları sıralar.

Önkoşullar: Vue 3 (Composition API veya Options API), Vite veya benzeri bir build aracı ve Vue DevTools'a aşinalık. Vue 2'ye özgü farklar ayrıca belirtilmiştir. Sürüm numarası verilmediği sürece API'lerin Vue 3'te mevcut olduğu varsayılmıştır; kesin sürüm bilgisi için resmi dokümantasyona bakın.


1. Reaktiviteyi Doğru Kur

Vue 3'ün Proxy tabanlı reaktivitesi, büyük nesnelerde derin (deep) takip maliyeti yaratır. Yalnızca ihtiyacın olan veriyi reaktif yap.

shallowRef ve shallowReactive kullanımı

Derin takip gerekmiyorsa sığ (shallow) alternatifleri tercih et:

import { shallowRef, triggerRef } from 'vue'

// Büyük bir liste veya üçüncü parti bir nesne tutuyorsan
const bigList = shallowRef([])

function replaceList(newList) {
  bigList.value = newList // referans değişince tetiklenir
}

function mutateInPlace() {
  bigList.value.push({ id: 1 })
  triggerRef(bigList) // manuel tetikleme gerekir
}

Beklenen davranış: shallowRef yalnızca .value referansı değiştiğinde güncellenir. İç elemanları değiştirdiğinde otomatik tetiklenmez; triggerRef çağrısı gerekir.

readonly ile kazara mutasyonu engelle

Alt bileşenlere geçirilen verinin yanlışlıkla değiştirilmesi, takip edilmesi zor render sorunlarına yol açar:

import { reactive, readonly } from 'vue'

const state = reactive({ count: 0 })
export const store = readonly(state)

Beklenen davranış: store.count = 1 denemesi geliştirme modunda uyarı verir; üretimde sessizce yok sayılır.


2. Render Maliyetini Azalt

v-if ve v-show ayrımı

  • v-if: DOM'dan tamamen kaldırır. Başlangıçta görünmeyen, nadiren değişen içerik için uygundur.
  • v-show: display: none ile gizler. Sık açılıp kapanan içerik için uygundur.

Her ikisini de aynı elemana koymak hatalıdır; v-if önceliklidir ve v-show beklenmedik davranır.

v-for içinde key seçimi

key olarak dizi indeksi kullanmak, liste sıralaması değiştiğinde DOM'un yanlış eşleşmesine yol açar. Kararlı, benzersiz bir kimlik kullan:

<!-- Kaçın -->
<li v-for="(item, i) in items" :key="i">{{ item.name }}</li>

<!-- Tercih et -->
<li v-for="item in items" :key="item.id">{{ item.name }}</li>

v-once ve v-memo

Bir kez render edilip değişmeyecek statik bloklar için v-once:

<footer v-once>{{ copyright }}</footer>

Büyük listelerde belirli koşullarda yeniden render'ı atlamak için v-memo:

<div v-for="item in list" :key="item.id" v-memo="[item.selected]">
  {{ item.name }} — {{ item.selected ? 'seçili' : 'değil' }}
</div>

Beklenen davranış: v-memo dizisindeki değerler değişmediği sürece alt ağaç yeniden render edilmez. Diziyi eksik tutmak, güncellenmesi gereken içeriğin donmasına yol açar.


3. Hesaplanmış ve İzlenen Değerleri Ayrıştır

computed ile watch farkı

Türetilmiş değerler için computed kullan. computed önbelleklenir; bağımlılıkları değişmedikçe yeniden hesaplanmaz.

import { ref, computed } from 'vue'

const items = ref([])
const total = computed(() => items.value.reduce((s, i) => s + i.price, 0))

Yan etki (ağ isteği, log, DOM manipülasyonu) gerekiyorsa watch uygundur. computed içinde yan etki üretmek öngörülemeyen çağrı sıklığına yol açar.

watchEffect yerine hedefli watch

watchEffect tüm erişilen reaktif değerleri takip eder. Bu, ilgisiz değişikliklerde gereksiz tetiklemeye neden olabilir. Kaynağı açıkça belirtmek daha güvenlidir:

import { watch } from 'vue'

watch(() => props.userId, async (id) => {
  // yalnızca userId değişince çalışır
}, { immediate: true })

4. Bileşen Yükleme Stratejisi

Asenkron bileşenler ve kod bölme

Başlangıç bundle'ından çıkarmak istediğin ağır bileşenleri defineAsyncComponent ile yükle:

import { defineAsyncComponent } from 'vue'

const HeavyChart = defineAsyncComponent(() => import('./HeavyChart.vue'))

Beklenen davranış: Bileşen ilk kez render edileceği zaman ayrı bir chunk indirilir. Vite/Rollup bu import'u otomatik olarak ayırır.

Yükleme sırasında geri bildirim için loadingComponent, hata durumu için errorComponent seçenekleri kullanılabilir.

Rota bazlı bölme

Vue Router ile rotaları tembel yükle:

const routes = [
  { path: '/rapor', component: () => import('./views/Report.vue') }
]

Bu, en etkili tek optimizasyondur: kullanıcı yalnızca ziyaret ettiği sayfanın kodunu indirir.


5. Liste Performansı

Yüzlerce satır render eden tablo veya listelerde iki temel yaklaşım vardır:

  1. Sanal kaydırma (virtual scrolling): Yalnızca görünen satırları DOM'a bas. Vue ekosisteminde vue-virtual-scroller gibi kütüphaneler bu iş için kullanılır; kütüphaneyi seçmeden önce bakım durumunu ve Vue 3 uyumluluğunu kontrol et.
  2. Sayfalama: Sunucu tarafında dilimleme. Basit ve öngörülebilir; ancak kullanıcı deneyimi sanal kaydırmadan farklıdır.

Ağır satır içeriklerinde her satırı ayrı bir bileşene ayırmak, güncellemelerin yalnızca etkilenen satırda çalışmasını sağlar.


6. Bundle Boyutu

Ağaç sallama (tree-shaking)

Vue 3, adlandırılmış export'lar üzerinden tree-shaking'e izin verir. Kullanmadığın API'leri import etmemek zaten varsayılan davranıştır; ancak üçüncü parti kütüphanelerde tüm paketi import etmek yaygın bir hatadır:

// Tüm kütüphaneyi çeker
import _ from 'lodash'

// Yalnızca ihtiyacı çeker
import debounce from 'lodash/debounce'

Üretim derlemesi

Geliştirme sırasında kullanılan uyarılar ve Vue DevTools kancaları üretim derlemesinde yer almaz. vite build çıktısını vite dev ile karşılaştırmak yanıltıcıdır; ölçümü her zaman üretim derlemesi üzerinde yap.

Görsel ve varlık optimizasyonu

  • Modern formatlar (WebP, AVIF) ve uygun srcset kullan.
  • Büyük görselleri build sırasında optimize etmek için Vite eklentilerinden yararlan.
  • Kritik olmayan CSS ve fontları geciktir.

7. Ölçüm: Optimizasyonun Önkoşulu

Tahmine dayalı optimizasyon genellikle yanlış yere emek harcar. Ölçmeden değiştirme.

  • Vue DevTools Performance sekmesi: Bileşen render sürelerini ve tetikleyicileri gösterir.
  • Tarayıcı Performance paneli: Uzun görevleri, layout thrashing'i ve paint maliyetini ortaya çıkarır.
  • Lighthouse / Web Vitals: LCP, INP ve CLS gibi kullanıcı odaklı metrikleri verir.
  • Bundle analizörü: Hangi paketin ne kadar yer kapladığını gösterir.

Ölçüm sonrası en pahalı bileşeni hedefle; mikro optimizasyonlarla başlamak zaman kaybıdır.


8. Yaygın Hatalar

  • Reaktiviteyi kırmak: reactive nesnesini yeniden atamak (state = { ... }) reaktiviteyi kaybettirir; Object.assign veya alan alan güncelleme tercih edilir.
  • computed içinde yan etki: Sonsuz döngü veya beklenmeyen çağrı sıklığına yol açar.
  • key olarak indeks: Liste sıralaması değiştiğinde yanlış DOM eşleşmesi.
  • v-if ve v-show birlikte: Öngörülemeyen davranış.
  • Gereksiz global state: Her veriyi merkezi store'a taşımak, bileşen izolasyonunu ve render verimliliğini azaltır.
  • watch içinde derin takip: { deep: true } büyük nesnelerde pahalıdır; belirli bir alanı izlemek daha verimlidir.

9. Güvenlik Etkileri

Optimizasyon kararları güvenliği dolaylı olarak etkileyebilir:

  • v-html: Performans için innerHTML kullanımına yönelmek XSS riskini artırır. Kullanıcı kaynaklı içeriği v-html ile basma; sanitize edilmiş çıktı kullan.
  • Asenkron bileşenler: Uzak sunucudan yüklenen bileşen modülleri güvenilir kaynaklardan gelmelidir; aksi halde kod enjeksiyonu riski doğar.
  • İstemci tarafı önbellek: Kullanıcıya özel verileri paylaşılan önbelleğe yazmak veri sızıntısına yol açabilir.
  • Kaynak haritaları (source maps): Üretimde açık bırakmak, kaynak kodun ve iç yapının ifşa olmasına neden olur.

10. Öncelik Sırası

Aynı anda her şeyi değiştirmek yerine şu sırayı izle:

  1. Ölçüm altyapısını kur (DevTools, Lighthouse, bundle analizörü).
  2. Rota bazlı kod bölmeyi uygula.
  3. En pahalı bileşenleri belirle ve reaktivite kullanımını gözden geçir.
  4. Liste ağırlıklı ekranlarda sanal kaydırma veya sayfalamayı değerlendir.
  5. Bundle'daki büyük bağımlılıkları azalt veya değiştir.
  6. Her adımdan sonra aynı metriklerle karşılaştır.

Optimizasyon, tek seferlik bir iş değil; ölçüm ve değişiklik döngüsüdür. Değişikliğin etkisini görmediğin sürece geri alma imkânını koru.

İLGİLİ İÇERİKLER

Bunları da Okumak İsteyebilirsiniz