Skip to content

Performans

Genel Bakış

Vue, çoğu yaygın kullanım senaryosunda manuel optimizasyona gerek kalmadan yüksek performans sunacak şekilde tasarlanmıştır. Ancak her zaman ek ince ayar gerektiren zorlu senaryolar vardır. Bu bölümde, bir Vue uygulamasında performans söz konusu olduğunda nelere dikkat etmeniz gerektiğini ele alacağız.

Öncelikle, web performansının iki temel boyutunu ele alalım:

  • Sayfa Yükleme Performansı: Uygulamanın ilk ziyarette ne kadar hızlı içerik gösterdiği ve etkileşime hazır hâle geldiği. Bu genellikle Largest Contentful Paint (LCP) ve Interaction to Next Paint gibi Web Vitals metrikleri kullanılarak ölçülür.

  • Güncelleme Performansı: Uygulamanın kullanıcı girdisine ne kadar hızlı yanıt verdiği. Örneğin, kullanıcı bir arama kutusuna yazdığında bir listenin ne kadar hızlı güncellendiği veya bir Tek Sayfalık Uygulamada (SPA) kullanıcı bir gezinme bağlantısına tıkladığında sayfanın ne kadar hızlı geçiş yaptığı.

İkisini de en üst düzeye çıkarmak ideal olsa da, farklı ön yüz mimarileri bu boyutlarda istenen performansa ulaşmanın ne kadar kolay olduğunu etkiler. Ayrıca, geliştirdiğiniz uygulamanın türü performans açısından neye öncelik vermeniz gerektiğini büyük ölçüde belirler. Bu nedenle, en iyi performansı sağlamanın ilk adımı, geliştirdiğiniz uygulama türü için doğru mimariyi seçmektir:

  • Vue'yu farklı şekillerde nasıl kullanabileceğinizi görmek için Vue'yu Kullanma Yolları sayfasına bakın.

  • Jason Miller, web uygulamalarının türlerini ve her birinin ideal uygulanışı / sunumunu Application Holotypes yazısında ele alır.

Profilleme Seçenekleri

Performansı iyileştirmek için öncelikle onu nasıl ölçeceğimizi bilmemiz gerekir. Bu konuda yardımcı olabilecek birçok iyi araç vardır:

Üretim dağıtımlarının yükleme performansını profillemek için:

Yerel geliştirme sırasında performansı profillemek için:

Sayfa Yükleme Optimizasyonları

Sayfa yükleme performansını optimize etmenin framework'ten bağımsız birçok yönü vardır — kapsamlı bir genel bakış için web.dev rehberine göz atabilirsiniz. Burada öncelikle Vue'ya özgü tekniklere odaklanacağız.

Doğru Mimariyi Seçmek

Kullanım senaryonuz sayfa yükleme performansına duyarlıysa, uygulamayı tamamen istemci tarafında çalışan bir SPA olarak dağıtmaktan kaçının. Sunucunuzun, kullanıcıların görmek istediği içeriği içeren HTML'i doğrudan göndermesi gerekir. Tamamen istemci tarafında gerçekleştirilen render işlemi, içeriğe ulaşma süresini (time-to-content) olumsuz etkiler. Bu durum, Sunucu Tarafında Render (SSR) veya Statik Site Üretimi (SSG) ile iyileştirilebilir. Vue ile SSR kullanımını öğrenmek için SSR rehberine bakın. Uygulamanızın yoğun etkileşim gereksinimleri yoksa, geleneksel bir backend sunucusu ile HTML'i render edip, ardından istemci tarafında Vue ile zenginleştirme yaklaşımını da tercih edebilirsiniz.

Ana uygulamanız bir SPA olmak zorundaysa ama pazarlama sayfaları da içeriyorsa (açılış, hakkımızda, blog), bunları ayrı dağıtın! Pazarlama sayfalarınız ideal olarak SSG kullanılarak minimum JS içeren statik HTML olarak dağıtılmalıdır.

Paket Boyutu ve Tree-shaking

Sayfa yükleme performansını iyileştirmenin en etkili yollarından biri daha küçük JavaScript paketleri yayınlamaktır. Vue kullanırken paket boyutunu azaltmanın birkaç yolu:

  • Mümkünse bir derleme adımı (build step) kullanın.

    • Vue'nun API'lerinin çoğu, modern bir derleme aracıyla paketlendiğinde "tree-shakable"dır. Örneğin, yerleşik <Transition> bileşenini kullanmıyorsanız nihai üretim paketine dahil edilmez. Tree-shaking, kaynak kodunuzdaki kullanılmayan diğer modülleri de kaldırabilir.

    • Bir derleme adımı kullanıldığında şablonlar önceden derlenir, böylece Vue derleyicisini tarayıcıya göndermeye gerek kalmaz. Bu, min+gzipped olarak 14kb JavaScript tasarrufu sağlar ve çalışma zamanı derleme maliyetini ortadan kaldırır.

  • Yeni bağımlılıklar eklerken boyuta dikkat edin! Gerçek uygulamalarda şişkin paketler çoğunlukla farkında olmadan ağır bağımlılıklar eklemekten kaynaklanır.

    • Bir derleme adımı kullanıyorsanız ES modül formatları sunan ve tree-shaking ile uyumlu bağımlılıkları tercih edin. Örneğin lodash yerine lodash-es kullanın.

    • Bir bağımlılığın boyutunu kontrol edin ve sağladığı işlevselliğin buna değip değmediğini değerlendirin. Bağımlılık tree-shaking ile uyumluysa gerçek boyut artışı, ondan içe aktardığınız API'lere bağlı olacaktır. Hızlı kontroller için bundlejs.com gibi araçlar kullanılabilir ama en doğru ölçümü her zaman kendi derleme kurulumunuzla yaparsınız.

  • Vue'yu öncelikle aşamalı geliştirme (progressive enhancement) için kullanıyor ve bir derleme adımından kaçınmayı tercih ediyorsanız, bunun yerine petite-vue'yu (yalnızca 6kb) kullanmayı düşünebilirsiniz.

Kod Bölme

Kod bölme, bir derleme aracının uygulama paketini birden fazla küçük parçaya (chunk) bölmesidir; bu parçalar daha sonra talep üzerine ya da paralel olarak yüklenebilir. Doğru kod bölmesiyle sayfa yüklemesinde gereken özellikler hemen indirilebilir; ek parçalar ise yalnızca gerektiğinde tembel yükleme (lazy loading) ile yüklenerek performansı artırır.

Rollup (Vite'ın temel aldığı) veya webpack gibi paketleyiciler, ESM dinamik içe aktarım söz dizimini algılayarak otomatik olarak bölünmüş parçalar oluşturabilir:

js
// lazy.js ve bağımlılıkları ayrı bir parçaya (chunk) ayrılır
// ve yalnızca `loadLazy()` çağrıldığında yüklenir.
function loadLazy() {
  return import('./lazy.js')
}

Tembel yükleme, ilk sayfa yüklemesinden hemen sonra ihtiyaç duyulmayan özellikler için en uygun yöntemdir. Vue uygulamalarında, bu yöntem Vue'nun Asenkron Bileşen özelliğiyle birlikte kullanılarak bileşen ağaçları için bölünmüş parçalar oluşturulabilir:

js
import { defineAsyncComponent } from 'vue'

// Foo.vue ve bağımlılıkları için ayrı bir parça oluşturulur.
// asenkron bileşen sayfada render edildiğinde
// yalnızca o anda talep üzerine indirilir.
const Foo = defineAsyncComponent(() => import('./Foo.vue'))

Vue Router kullanan uygulamalarda, rota bileşenleri için tembel yüklemenin kullanılması şiddetle önerilir. Vue Router'ın defineAsyncComponent'ten ayrı, tembel yüklemeye yönelik açık desteği vardır. Ayrıntılar için Tembel Yüklenen Rotalar sayfasına bakın.

Güncelleme Optimizasyonları

Props Kararlılığı

Vue'da bir alt bileşen, aldığı props'lardan en az biri değiştiğinde güncellenir. Aşağıdaki örneği inceleyelim:

template
<ListItem
  v-for="item in list"
  :id="item.id"
  :active-id="activeId" />

<ListItem> bileşeninin içinde, o anda aktif olan öğenin kendisi olup olmadığını belirlemek için id ve activeId props'larını kullanır. Bu çalışır ama sorun şudur: activeId her değiştiğinde listedeki her <ListItem> güncellenmek zorunda kalır!

İdeal olarak yalnızca aktif durumu değişen öğeler güncellenmelidir. Bunu, aktif durum hesaplamasını üst bileşene taşıyıp <ListItem>'ın doğrudan bir active prop'u kabul etmesini sağlayarak başarabiliriz:

template
<ListItem
  v-for="item in list"
  :id="item.id"
  :active="item.id === activeId" />

Artık çoğu bileşen için active prop'u, activeId değiştiğinde aynı kalır; bu yüzden güncellenmelerine gerek kalmaz. Genel olarak amaç, alt bileşenlere geçirilen props'ları olabildiğince kararlı tutmaktır.

v-once

v-once, çalışma zamanı verisine dayanan ama hiçbir zaman güncellenmesi gerekmeyen içeriği render etmek için kullanılabilen yerleşik bir yönergedir. Üzerinde kullanıldığı tüm alt ağaç, gelecek tüm güncellemelerde atlanır. Ayrıntılar için API referansına bakın.

v-memo

v-memo, büyük alt ağaçların veya v-for listelerinin güncellenmesini koşullu olarak atlamak için kullanılabilen yerleşik bir yönergedir. Ayrıntılar için API referansına bakın.

Computed Kararlılığı

Vue 3.4 ve sonrasında, bir computed property yalnızca hesaplanan değeri bir öncekinden farklı olduğunda etki tetikler. Örneğin aşağıdaki isEven computed'ı yalnızca döndürülen değer true'dan false'a (veya tersine) değiştiğinde etki tetikler:

js
const count = ref(0)
const isEven = computed(() => count.value % 2 === 0)

watchEffect(() => console.log(isEven.value)) // true

// computed değeri `true` kaldığı için yeni log tetiklemez
count.value = 2
count.value = 4

Bu, gereksiz etki tetiklemelerini azaltır ama ne yazık ki computed her hesaplamada yeni bir nesne oluşturuyorsa çalışmaz:

js
const computedObj = computed(() => {
  return {
    isEven: count.value % 2 === 0
  }
})

Her seferinde yeni bir nesne oluşturulduğu için, yeni değer teknik olarak her zaman eski değerden farklıdır. isEven özelliği aynı kalsa bile, Vue eski ve yeni değerin derin karşılaştırmasını yapmadan bunu bilemez. Böyle bir karşılaştırma maliyetli olabilir ve büyük ihtimalle buna değmez.

Bunun yerine, yeni değeri eski değerle elle karşılaştırarak ve hiçbir şeyin değişmediğinden eminsek koşullu olarak eski değeri döndürerek bunu optimize edebiliriz:

js
const computedObj = computed((oldValue) => {
  const newValue = {
    isEven: count.value % 2 === 0
  }
  if (oldValue && oldValue.isEven === newValue.isEven) {
    return oldValue
  }
  return newValue
})

Playground'da deneyin

Karşılaştırıp eski değeri döndürmeden önce her zaman tam hesaplamayı yapmanız gerektiğini unutmayın; böylece her çalıştırmada aynı bağımlılıklar toplanabilir.

Genel Optimizasyonlar

Aşağıdaki ipuçları hem sayfa yükleme hem de güncelleme performansını etkiler.

Büyük Listeleri Sanallaştırma

Tüm ön yüz uygulamalarında en yaygın performans sorunlarından biri büyük listelerin render edilmesidir. Bir framework ne kadar performanslı olursa olsun, binlerce öğe içeren bir listeyi render etmek, tarayıcının işlemesi gereken DOM düğümlerinin yüksek sayısı nedeniyle yavaş olacaktır.

Ancak tüm bu düğümleri en başta render etmek zorunda değiliz. Çoğu durumda kullanıcının ekran boyutu büyük listemizin yalnızca küçük bir alt kümesini gösterebilir. Liste sanallaştırması (list virtualization) ile performansı önemli ölçüde artırabiliriz; bu teknik, büyük bir listede yalnızca o anda görünüm alanında veya ona yakın olan öğeleri render etmektir.

Liste sanallaştırmasını uygulamak kolay değildir; neyse ki doğrudan kullanabileceğiniz mevcut topluluk kütüphaneleri vardır:

Büyük Değişmez Yapılar için Tepkisellik Ek Yükünü Azaltma

Vue'nun tepkisellik sistemi varsayılan olarak derindir. Bu durum durum yönetimini sezgisel kılarken, veri boyutu büyük olduğunda belirli bir ek yük oluşturur; çünkü her özellik erişimi, bağımlılık izleme yapan proxy tuzaklarını tetikler. Bu genellikle, tek bir render'ın 100.000'den fazla özelliğe erişmesi gereken derin iç içe nesneler içeren büyük dizilerle uğraşırken fark edilir; dolayısıyla yalnızca çok özel kullanım senaryolarını etkilemelidir.

Vue, shallowRef() ve shallowReactive() kullanarak derin tepkisellikten çıkış yapmak için bir kaçış kapısı sunar. Sığ API'ler yalnızca kök düzeyde tepkisel olan bir durum oluşturur ve tüm iç içe nesneleri olduğu gibi sunar. Bu, iç içe özellik erişimini hızlı tutar; karşılığında ise artık tüm iç içe nesneleri değişmez (immutable) olarak ele almamız gerekir ve güncellemeler yalnızca kök durumun değiştirilmesiyle tetiklenebilir:

js
const shallowArray = shallowRef([
  /* derin nesnelerden oluşan büyük liste */
])

// bu güncelleme tetiklemez...
shallowArray.value.push(newObject)
// bu tetikler:
shallowArray.value = [...shallowArray.value, newObject]

// bu güncelleme tetiklemez...
shallowArray.value[0].foo = 1
// bu tetikler:
shallowArray.value = [
  {
    ...shallowArray.value[0],
    foo: 1
  },
  ...shallowArray.value.slice(1)
]

Gereksiz Bileşen Soyutlamalarından Kaçının

Bazen daha iyi soyutlama veya kod organizasyonu için render etmeyen bileşenler (renderless components) veya yüksek dereceli bileşenler (yani diğer bileşenleri ek props ile render eden bileşenler) oluşturabiliriz. Bunda yanlış bir şey yok; ancak bileşen örneklerinin düz DOM düğümlerinden çok daha maliyetli olduğunu ve soyutlama desenleri nedeniyle bunlardan çok fazlasını oluşturmanın performans maliyetine yol açacağını unutmayın.

Yalnızca birkaç örneği azaltmanın belirgin bir etkisi olmayacağını unutmayın; bu nedenle bileşen uygulamada yalnızca birkaç kez render ediliyorsa endişelenmeyin. Bu optimizasyonu değerlendirmek için en uygun senaryo yine büyük listelerdir. Her öğe bileşeninin birçok alt bileşen içerdiği 100 öğelik bir liste düşünün. Burada gereksiz bir bileşen soyutlamasını kaldırmak yüzlerce bileşen örneğinin azalmasına yol açabilir.

Performans has loaded