Nuxt 3 ile SSR ve Performanslı Vue Uygulamaları
Nuxt 3, Vue 3 üzerine kurulu bir meta çatıdır ve sunucu tarafı render (SSR), statik üretim ve hibrit render seçeneklerini tek bir proje yapısında sunar. Bu rehber, orta seviye Vue bilgisine sahip geliştiricilerin SSR davranışını doğru kurgulamasına ve gereksiz istemci yükünden kaçınmasına odaklanır. Sürüm numarası vermiyorum; Nuxt 3 hattındaki davranışları anlatıyorum ve API'lerin sürümle değişebileceğini varsaymanızı öneririm. Kurulum ve komutlar için resmî dokümantasyonu esas alın.
Önkoşullar
- Node.js'in Nuxt 3'ün desteklediği bir LTS sürümü
- Vue 3 Composition API ve
ref/computedgibi temel kavramlar - npm, pnpm veya yarn'dan biri
- HTTP, DNS ve tarayıcı önbelleği hakkında temel bilgi
Nuxt 3'te Render Modları
Nuxt 3'te render davranışı tek bir bayrakla değil, birkaç eksende belirlenir:
- SSR (varsayılan): İlk istekte HTML sunucuda üretilir, ardından istemci tarafında hidrasyon yapılır.
- SSG / prerender: Sayfalar derleme zamanında HTML olarak üretilir.
- İstemci tarafı render: Belirli rotalar veya bileşenler yalnızca tarayıcıda çalışır.
- Hibrit: Rota bazında
prerender,ssrgibi kurallarla karışık davranış tanımlanabilir.
Hangi modu seçeceğiniz içeriğin kişiselleştirilmiş olup olmamasına, güncellik gereksinimine ve SEO beklentisine bağlıdır. Kullanıcıya özel paneller için saf SSR çoğu zaman gereksizdir; pazarlama sayfaları için ise SSR veya prerender genellikle daha uygundur.
Proje Yapısı ve Temel Kavramlar
Nuxt 3'te dizin yapısı davranışı belirler:
pages/: Dosya tabanlı yönlendirme.pages/index.vuekök rotayı tanımlar.components/: Otomatik içe aktarılan bileşenler.composables/: Otomatik içe aktarılan composable'lar.server/api/: Sunucu rotaları. Buradaki dosyalar istemci paketine dahil olmaz.plugins/: Uygulama başlangıcında çalışan kod. Varsayılan olarak hem sunucuda hem istemcide çalışır.middleware/: Rota bazlı yönlendirme öncesi mantık.
useAsyncData, useFetch ve $fetch arasındaki ayrım kritik:
$fetch, doğrudan bir HTTP isteği yapar; SSR sırasında sunucudan, istemcide tarayıcıdan çalışır. Bileşen içinde çıplak kullanılırsa hem sunucuda hem istemcide tekrar tetiklenebilir.useAsyncDataveuseFetch, Nuxt'in veri katmanını kullanır; SSR'de üretilen veriyi istemciye aktararak hidrasyon sırasında gereksiz isteği engeller.
Veri çekme işini her zaman useAsyncData veya useFetch üzerinden yapmak, çift istek ve hidrasyon uyumsuzluğu sorunlarının çoğunu baştan önler.
Adım Adım: SSR Uyumlu Veri Çekme
1. Sayfa içinde useFetch kullanın
<script setup>
const { data, error, pending } = await useFetch('/api/posts', {
key: 'posts-list',
})
</script>
<template>
<div>
<p v-if="pending">Yükleniyor…</p>
<p v-else-if="error">Hata: {{ error.message }}</p>
<ul v-else>
<li v-for="post in data" :key="post.id">{{ post.title }}</li>
</ul>
</div>
</template>
Beklenen davranış: Sunucu istek sırasında /api/posts çağrılır, dönen veri HTML ile birlikte istemciye taşınır ve hidrasyonda aynı istek tekrar edilmez. key vererek aynı verinin farklı bileşenlerde paylaşılmasını kontrol edebilirsiniz.
2. Sunucu rotasını server/api/ altında tanımlayın
// server/api/posts.js
export default defineEventHandler(async (event) => {
const config = useRuntimeConfig(event)
const res = await $fetch(`${config.apiBase}/posts`)
return res
})
Beklenen davranış: İstemci apiBase gibi gizli değerleri görmez; istek sunucudan yapılır. useRuntimeConfig ile ortam değişkenlerini yönetmek, gizli anahtarları istemci paketinden uzak tutar.
3. Kullanıcıya özel veriyi SSR'den ayırın
Oturum, çerez veya kullanıcı kimliğine bağlı veriler SSR sırasında üretilirse önbelleğe yanlışlıkla dahil olabilir. Bu tür verileri useAsyncData içinde server: false ile yalnızca istemciye bırakmak veya ayrı bir sunucu rotasında Cache-Control: private başlığı döndürmek daha güvenlidir.
const { data } = await useAsyncData('me', () => $fetch('/api/me'), {
server: false,
})
Beklenen davranış: Bu veri SSR HTML'ine girmez, yalnızca tarayıcıda çekilir.
Performans: Yükleme ve Önbellek
Kod bölme ve tembel bileşenler
Nuxt 3, rota bazlı kod bölmeyi varsayılan olarak uygular. Ağır bileşenleri yalnızca gerektiğinde yüklemek için Lazy ön ekini kullanabilirsiniz:
<LazyHeavyChart v-if="showChart" />
Beklenen davranış: Bileşen, koşul sağlanana kadar indirilmez. Bu, ilk yükleme boyutunu düşürür.
Görseller
Nuxt ekosistemindeki görsel modülüyle boyut, format ve srcset üretimini yönetebilirsiniz. Modül adı ve API'leri sürüme göre değişebileceğinden resmî dokümantasyondan doğrulayın. Genel kural: hero görselleri için priority/preload, ekran dışı görseller için lazy kullanın.
Önbellek başlıkları
Sunucu rotalarında dönen yanıtlara Cache-Control eklemek, tekrarlayan SSR maliyetini azaltır. Kişiselleştirilmiş yanıtları public yapmayın. İçerik herkese açıksa ve güncellik toleransı varsa s-maxage ve stale-while-revalidate kombinasyonu tipik bir yaklaşımdır:
export default defineEventHandler((event) => {
setHeader(event, 'Cache-Control', 'public, s-maxage=60, stale-while-revalidate=300')
return { ok: true }
})
Beklenen davranış: Aynı URL için tekrarlayan istekler kısa süreliğine önbellekten karşılanır; arka planda tazelenir.
Yaygın Hatalar
- Bileşen içinde çıplak
$fetchkullanmak: Aynı isteğin sunucu ve istemcide tekrarlanmasına, hidrasyon uyumsuzluğuna yol açar.useAsyncData/useFetchtercih edin. window,document,localStorage'a doğrudan erişmek: Bu API'ler SSR sırasında yoktur. ErişimionMountedveyaimport.meta.clientkontrolü içine alın.process.envile gizli değer okumak: İstemci paketine sızabilir.useRuntimeConfigkullanın ve hassas değerleriruntimeConfigaltında sunucu tarafında tutun.- Zaman damgası veya rastgele değerleri şablonda üretmek: Sunucu ve istemci çıktısı farklı olur; hidrasyon uyarısı alırsınız. Bu tür değerleri
onMountedsonrası hesaplayın. - Kullanıcıya özel veriyi paylaşımlı önbelleğe almak: Başka kullanıcıların verisini sızdırabilir. Kişiselleştirilmiş yanıtlarda
Cache-Control: privateveyaserver: falsekullanın. useStateyerine modül seviyesi değişken kullanmak: SSR'de istekler arası veri sızıntısına yol açabilir. Paylaşılan durum içinuseStatetercih edin.
Güvenlik Notları
- Sunucu rotalarını doğrulama katmanı olarak görün: İstemciden gelen tüm girdileri doğrulayın; istemci doğrulaması güven sınırı değildir.
- Kimlik doğrulamayı sunucuda tutun: Oturum belirteçlerini istemciye açık şekilde göndermek yerine HTTP-only çerezler ve sunucu tarafı kontroller tercih edilir.
- CSRF ve CORS: Durum değiştiren uç noktalar için CSRF koruması, tarayıcıdan çağrılan API'ler için sıkı CORS politikaları uygulayın.
- SSRF riski: Sunucu rotası içinde kullanıcıdan gelen URL'yi doğrudan
$fetchile çağırmayın; beyaz liste kullanın. v-html: Kullanıcı içeriğini doğrudan render etmek XSS'e yol açar. Gerekiyorsa sunucu tarafında temizleyin.- Ortam değişkenleri: Gizli anahtarları yalnızca
runtimeConfig'in sunucu tarafında tanımlı alanlarında tutun; istemciye açık alanlara koymayın.
Dağıtım ve İzleme
SSR davranışı barındırma ortamına göre değişir. Node.js sunucusunda çalıştırıyorsanız süreç yönetimi ve ters proxy yapılandırması gerekir. Edge çalışma zamanlarında Node API'lerinin bir kısmı bulunmaz; sunucu kodunuzu buna göre yazın. Üretimde şunları izlemek faydalıdır:
- Sunucu tarafı render süresi (p50/p95)
- Sunucu rotalarının hata oranı
- Önbellek isabet oranı
- İstemci tarafında hidrasyon uyarıları
Bu metrikler, SSR'in gerçekten fayda sağlayıp sağlamadığını gösterir. Ölçüm yapmadan eklenen önbellek veya prerender kuralları, güncellik sorunlarına veya yanlış içerik sunumuna yol açabilir.
Kapanış
Nuxt 3'te performanslı SSR, büyük ölçüde doğru veri katmanını kullanmaya ve sunucu/istemci sınırını net tutmaya dayanır. useAsyncData ve useFetch ile veri çekmek, gizli değerleri runtimeConfig üzerinden yönetmek, kişiselleştirilmiş veriyi SSR dışında tutmak ve önbellek başlıklarını bilinçli ayarlamak, hem hidrasyon sorunlarını hem de gereksiz sunucu yükünü azaltır. Render modunu içeriğin doğasına göre seçin; ölçüm yapmadan varsayımlarla ilerlemeyin.