Nuxt 3: SSR ve statik üretim, eksiksiz rehber

Nuxt 3 ile SSR ve statik üretimi tüm yönleriyle öğrenin. useFetch'ten route rules'a, Vue.js uygulamalarınızın performansını optimize edin.

Nuxt 3 ve Vue.js ile sunucu tarafı render ve statik üretimi gösteren illüstrasyon

Nuxt 3, farklı kullanım senaryolarına uygun çoklu render modları sunarak Vue.js uygulamalarının inşa şeklini dönüştürür. Server-Side Rendering (SSR), statik üretim ve hibrit render arasında framework, performans ve SEO optimizasyonu için dikkat çekici bir esneklik sağlar.

Ön gereksinimler

Bu eğitim Vue 3 ve Composition API hakkında temel bilgi gerektirir. Sunucu tarafı render kavramlarına aşinalık faydalı olur ancak zorunlu değildir; temel bilgiler rehber boyunca açıklanır.

Nuxt 3 render modlarını anlamak

Koda dalmadan önce mevcut render modları arasındaki farkları anlamak şarttır. Her mod; performans, SEO ve kullanıcı deneyimi açısından belirli ihtiyaçlara karşılık gelir.

SSR (Server-Side Rendering) her istek için sunucuda HTML üretir. Statik üretim (SSG) build zamanında tüm sayfaları önceden üretir. Hibrit mod, bu yaklaşımları sayfa bazında birleştirmeyi sağlar.

nuxt.config.tstypescript
// Farklı render modlarının yapılandırması
export default defineNuxtConfig({
  // SSR varsayılan olarak etkin (SEO için önerilir)
  ssr: true,

  // Statik üretim: tüm sayfaları önceden render eder
  // Build için 'npm run generate' kullanın
  // target: 'static', // Nuxt 2 söz dizimi

  // Hibrit mod: rota bazında yapılandırılabilir
  routeRules: {
    // Ana sayfa: önceden render edilmiş ve önbelleğe alınmış
    '/': { prerender: true },
    // Blog: statik üretim
    '/blog/**': { prerender: true },
    // Dashboard: yalnızca istemci tarafı render
    '/dashboard/**': { ssr: false },
    // API: pre-render yok
    '/api/**': { prerender: false }
  }
})

Bu yapılandırma hibrit modun gücünü gösterir: uygulamanın her bölümü kendi ihtiyaçlarına en uygun render modunu kullanır.

useFetch ve useAsyncData ile veri çekme

Nuxt 3, izomorfik şekilde veri çekmek için iki ana composable sunar. Bu composable'lar hem sunucu hem istemci tarafında çalışır ve hidrasyonu otomatik yönetir.

useFetch, HTTP çağrılarını basitleştiren useAsyncData etrafındaki bir wrapper'dır. useAsyncData ileri seviye senaryolarda daha fazla kontrol sunar.

vue
<script setup lang="ts">
// pages/blog/[slug].vue
// useFetch ile makale detay sayfası

// Rota parametresini al
const route = useRoute()

// useFetch: otomatik veri çekme
// Veriler sunucu tarafında çekilir ve istemcide hidre edilir
const { data: article, pending, error } = await useFetch(
  `/api/articles/${route.params.slug}`,
  {
    // Önbellek ve tekilleştirme için benzersiz anahtar
    key: `article-${route.params.slug}`,
    // Gerekirse veriyi dönüştür
    transform: (response) => response.data,
    // Önbellek seçenekleri
    getCachedData: (key) => {
      // Verinin önbellekte olup olmadığını kontrol et
      const nuxtApp = useNuxtApp()
      return nuxtApp.payload.data[key]
    }
  }
)

// Navigasyonla hata yönetimi
if (error.value) {
  throw createError({
    statusCode: 404,
    message: 'Makale bulunamadı'
  })
}
</script>

<template>
  <div>
    <div v-if="pending" class="loading">
      Makale yükleniyor...
    </div>
    <article v-else-if="article">
      <h1>{{ article.title }}</h1>
      <div v-html="article.content" />
    </article>
  </div>
</template>

Daha fazla kontrol gerektiren durumlarda useAsyncData herhangi bir asenkron fonksiyon çalıştırmaya izin verir.

vue
<script setup lang="ts">
// pages/products/index.vue
// useAsyncData ve filtrelerle ürün listesi

const route = useRoute()

// useAsyncData: fetch mantığı üzerinde tam kontrol
const { data: products, refresh } = await useAsyncData(
  'products-list',
  async () => {
    // Gerekirse birden fazla kaynaktan çek
    const [productsResponse, categoriesResponse] = await Promise.all([
      $fetch('/api/products', {
        query: {
          category: route.query.category,
          sort: route.query.sort || 'date'
        }
      }),
      $fetch('/api/categories')
    ])

    // Verileri birleştir ve dönüştür
    return {
      products: productsResponse.data,
      categories: categoriesResponse.data,
      total: productsResponse.meta.total
    }
  },
  {
    // Query params değiştiğinde yenile
    watch: [() => route.query]
  }
)

// Manuel yenileme fonksiyonu
const updateFilters = async (newCategory: string) => {
  await navigateTo({
    query: { ...route.query, category: newCategory }
  })
}
</script>

Bu composable'lar çift fetch'i önler: sunucuda alınan veriler HTML payload'una serileştirilir ve istemci hidrasyonu sırasında yeniden kullanılır.

Server hooks ile SSR'ı özelleştirmek

Nuxt 3'ün SSR'ı server hooks aracılığıyla özelleştirilebilir. Bu hook'lar, varsayılan davranışı değiştirmek için render döngüsünün farklı aşamalarına müdahale etmeye olanak tanır.

server/plugins/render-hooks.tstypescript
// SSR render'ını özelleştirmek için sunucu eklentisi

export default defineNitroPlugin((nitroApp) => {
  // Her sayfanın render'ından önce çalışan hook
  nitroApp.hooks.hook('render:html', (html, { event }) => {
    // Script veya metadata enjekte et
    html.head.push(`
      <script>
        // Analitik veya global yapılandırma
        window.__APP_CONFIG__ = {
          environment: '${process.env.NODE_ENV}',
          apiUrl: '${process.env.API_URL}'
        }
      </script>
    `)
  })

  // Render önbelleğinin yönetimi için hook
  nitroApp.hooks.hook('render:response', (response, { event }) => {
    // Özel önbellek başlıkları ekle
    const path = event.path

    if (path.startsWith('/blog/')) {
      // Blog makaleleri için uzun önbellek
      response.headers['Cache-Control'] = 'public, max-age=3600, s-maxage=86400'
    } else if (path.startsWith('/api/')) {
      // API'ler için önbellek yok
      response.headers['Cache-Control'] = 'no-store'
    }
  })
})
SSR performansı

render:response hook'u HTTP önbellek stratejilerini uygulamak için idealdir. SSR'ı Cache-Control başlıklarına saygı gösteren bir CDN ile birleştirmek, önceden render edilmiş sayfaları sunmaya devam ederken bunları geçersiz kılma yeteneğini korur.

nuxt generate ile statik üretim

Statik üretim, build zamanında tüm sayfaları önceden inşa eder. Bu yaklaşım blog, dokümantasyon veya pazarlama siteleri gibi sabit içerikli siteler için idealdir.

Dinamik rotalar için Nuxt'un üretilecek tüm URL'leri bilmesi gerekir. prerender:routes hook'u bu rotaları programatik olarak tanımlamayı sağlar.

nuxt.config.tstypescript
// Statik üretim için eksiksiz yapılandırma

export default defineNuxtConfig({
  // Statik üretimi etkinleştir
  nitro: {
    prerender: {
      // Otomatik link tarayıcısını etkinleştir
      crawlLinks: true,
      // Her zaman dahil edilecek rotalar
      routes: ['/', '/about', '/contact'],
      // Belirli rotaları yoksay
      ignore: ['/admin', '/api']
    }
  },

  hooks: {
    // Dinamik rotaları üretmek için hook
    async 'prerender:routes'(ctx) {
      // Makaleleri API veya DB'den çek
      const articles = await fetch('https://api.example.com/articles')
        .then(res => res.json())

      // Makale rotalarını ekle
      for (const article of articles) {
        ctx.routes.add(`/blog/${article.slug}`)
      }

      // Kategorileri çek
      const categories = await fetch('https://api.example.com/categories')
        .then(res => res.json())

      for (const category of categories) {
        ctx.routes.add(`/category/${category.slug}`)
      }
    }
  }
})

Çok sayfalı projelerde otomatik tarayıcı yetersiz kalabilir. İşte ayrı bir yapılandırma dosyasıyla daha sağlam bir yaklaşım.

server/utils/generate-routes.tstypescript
// Dinamik rota listesini üretmek için yardımcı işlev

import { prisma } from './prisma'

export async function getAllStaticRoutes(): Promise<string[]> {
  const routes: string[] = []

  // Blog makaleleri
  const articles = await prisma.article.findMany({
    where: { published: true },
    select: { slug: true, category: { select: { slug: true } } }
  })

  for (const article of articles) {
    routes.push(`/blog/${article.category.slug}/${article.slug}`)
  }

  // Ürün sayfaları
  const products = await prisma.product.findMany({
    where: { active: true },
    select: { slug: true }
  })

  for (const product of products) {
    routes.push(`/products/${product.slug}`)
  }

  // Etiket sayfaları
  const tags = await prisma.tag.findMany({
    select: { slug: true }
  })

  for (const tag of tags) {
    routes.push(`/tags/${tag.slug}`)
  }

  return routes
}

Vue.js / Nuxt.js mülakatlarında başarılı olmaya hazır mısın?

İnteraktif simülatörler, flashcards ve teknik testlerle pratik yap.

routeRules ile hibrit render

Hibrit render Nuxt 3'ün öne çıkan özelliğidir. Her rota için farklı render kuralları tanımlamayı ve SSR ile SSG'nin en iyi yönlerini birleştirmeyi sağlar.

nuxt.config.tstypescript
// Gelişmiş hibrit render yapılandırması

export default defineNuxtConfig({
  routeRules: {
    // Pazarlama sayfaları: önceden render edilmiş ve uzun süre önbelleklenmiş
    '/': { prerender: true },
    '/pricing': { prerender: true },
    '/features/**': { prerender: true },

    // Blog: ISR (Incremental Static Regeneration)
    // Saatte bir yeniden doğrulama
    '/blog/**': {
      isr: 3600,
      prerender: true
    },

    // Dokümantasyon: yeniden doğrulamalı CDN önbelleği
    '/docs/**': {
      swr: 86400, // Stale-while-revalidate
      prerender: true
    },

    // E-ticaret: kısa önbellekli SSR
    '/products/**': {
      ssr: true,
      cache: {
        maxAge: 60,
        staleMaxAge: 300
      }
    },

    // Sepet ve ödeme: yalnızca istemci tarafı
    '/cart': { ssr: false },
    '/checkout/**': { ssr: false },

    // Dashboard: SPA modu
    '/dashboard/**': {
      ssr: false,
      // Pre-rendering'i devre dışı bırak
      prerender: false
    },

    // API rotaları: varsayılan olarak önbellek yok
    '/api/**': {
      cors: true,
      headers: {
        'Access-Control-Allow-Methods': 'GET,POST,PUT,DELETE'
      }
    }
  }
})

Bu yapılandırma tipik bir modern uygulama mimarisini gösterir: kamuya açık sayfalar SEO için SSG ile optimize edilirken etkileşimli bölümler istemci tarafı render kullanır.

Veri önbelleğiyle performans optimizasyonu

Sayfa önbelleğinin ötesinde Nuxt 3, çekilen verileri önbelleklemeyi sağlar. Bu strateji API'ler üzerindeki yükü azaltır ve yanıt sürelerini iyileştirir.

server/api/articles/[slug].get.tstypescript
// Veri önbellekli API endpoint'i

import { getArticleBySlug } from '~/server/utils/articles'

export default defineCachedEventHandler(
  async (event) => {
    const slug = getRouterParam(event, 'slug')

    if (!slug) {
      throw createError({
        statusCode: 400,
        message: 'Slug eksik'
      })
    }

    const article = await getArticleBySlug(slug)

    if (!article) {
      throw createError({
        statusCode: 404,
        message: 'Makale bulunamadı'
      })
    }

    return article
  },
  {
    // Slug tabanlı önbellek anahtarı
    getKey: (event) => `article-${getRouterParam(event, 'slug')}`,
    // Önbellek süresi: 1 saat
    maxAge: 3600,
    // Stale-while-revalidate: güncelleme sırasında eski önbelleği sun
    staleMaxAge: 7200,
    // Etiket tabanlı geçersiz kılma
    tags: ['articles']
  }
)

İçerik değiştiğinde önbelleği geçersiz kılmak için Nuxt bir etiket sistemi sunar.

server/api/articles/[slug].put.tstypescript
// Önbellek geçersiz kılmalı makale güncellemesi

import { updateArticle } from '~/server/utils/articles'

export default defineEventHandler(async (event) => {
  const slug = getRouterParam(event, 'slug')
  const body = await readBody(event)

  // Makaleyi güncelle
  const article = await updateArticle(slug, body)

  // Bu makale için önbelleği geçersiz kıl
  await useStorage('cache').removeItem(`nitro:handlers:article-${slug}`)

  // Veya etiket tabanlı geçersiz kılma (tüm makaleler)
  // await useStorage('cache').clear('articles')

  return article
})
Dağıtık önbellek

Birden fazla örnekli üretimde bellek içi önbellek yetersiz kalır. Örnekler arasında tutarlılığı sağlamak için Nitro yapılandırması üzerinden Redis veya başka bir dağıtık sistemin kurulması önerilir.

SEO ve metadata yönetimi

SSR, metadata'yı sunucu tarafında üreterek SEO optimizasyonuna olanak tanır. Nuxt 3, meta etiketleri dinamik olarak yönetmek için birkaç yaklaşım sunar.

vue
<script setup lang="ts">
// pages/blog/[slug].vue
// SEO için optimize edilmiş blog sayfası

const route = useRoute()

const { data: article } = await useFetch(`/api/articles/${route.params.slug}`)

// Makale tabanlı dinamik SEO yapılandırması
useSeoMeta({
  title: article.value?.title,
  description: article.value?.excerpt,
  ogTitle: article.value?.title,
  ogDescription: article.value?.excerpt,
  ogImage: article.value?.coverImage,
  ogType: 'article',
  twitterCard: 'summary_large_image',
  twitterTitle: article.value?.title,
  twitterDescription: article.value?.excerpt,
  twitterImage: article.value?.coverImage
})

// Google için yapılandırılmış veriler
useHead({
  script: [
    {
      type: 'application/ld+json',
      innerHTML: JSON.stringify({
        '@context': 'https://schema.org',
        '@type': 'Article',
        headline: article.value?.title,
        description: article.value?.excerpt,
        image: article.value?.coverImage,
        datePublished: article.value?.publishedAt,
        dateModified: article.value?.updatedAt,
        author: {
          '@type': 'Organization',
          name: 'SharpSkill'
        }
      })
    }
  ]
})
</script>

Statik sayfalar için metadata doğrudan bileşende tanımlanabilir.

vue
<script setup lang="ts">
// pages/about.vue
// SEO'lu statik sayfa

definePageMeta({
  title: 'Hakkında'
})

useSeoMeta({
  title: 'SharpSkill Hakkında | Teknik mülakat hazırlığı',
  description: 'SharpSkill\'i keşfedin: teknik mülakat hazırlık platformu. Misyon: geliştiricilerin teknik mülakatlarda başarılı olmasına yardımcı olmak.',
  ogTitle: 'SharpSkill Hakkında',
  ogDescription: 'Teknik mülakat hazırlık platformu',
  ogImage: '/images/og-about.webp'
})
</script>

Dağıtım ve üretim hususları

Dağıtım seçimi kullanılan render moduna bağlıdır. İşte başlıca seçenekler ve yapılandırmaları.

nuxt.config.tstypescript
// Farklı dağıtım ortamları için yapılandırma

export default defineNuxtConfig({
  nitro: {
    // Hedef platforma göre preset
    // preset: 'vercel', // Vercel
    // preset: 'netlify', // Netlify
    // preset: 'cloudflare-pages', // Cloudflare
    // preset: 'node-server', // Klasik Node.js

    // Üretimde Node.js için yapılandırma
    preset: 'node-server',

    // Yanıt sıkıştırması
    compressPublicAssets: true,

    // Önbellek deposu yapılandırması
    storage: {
      cache: {
        driver: 'redis',
        url: process.env.REDIS_URL
      }
    }
  },

  // Çalışma zamanı ortam değişkenleri
  runtimeConfig: {
    // Sırlar (istemciye sunulmaz)
    apiSecret: process.env.API_SECRET,
    // Genel yapılandırma
    public: {
      apiBase: process.env.NUXT_PUBLIC_API_BASE || '/api'
    }
  }
})

Statik dağıtım için npm run generate komutu, herhangi bir statik dosya barındırıcısında dağıtıma hazır bir .output/public klasörü oluşturur.

bash
# Statik üretim
npm run generate

# .output/public içeriği şunlara dağıtılabilir:
# - Vercel (otomatik tespit)
# - Netlify (otomatik yapılandırma)
# - GitHub Pages
# - S3 + CloudFront
# - Herhangi bir CDN veya statik dosya sunucusu

Pratik yapmaya başla!

Mülakat simülatörleri ve teknik testlerle bilgini test et.

Sonuç

Nuxt 3, Vue.js uygulamalarını render etmek için olağanüstü bir esneklik sunar. SSR, SSG ve hibrit render arasındaki seçim her projenin özel ihtiyaçlarına bağlıdır.

Önemli noktalar:

SSR: iyi SEO gerektiren dinamik içerikler için ideal (e-ticaret, haber siteleri)

SSG: sabit içerikler için mükemmel (bloglar, dokümantasyon, pazarlama siteleri)

Hibrit: çeşitli ihtiyaçlara sahip karmaşık uygulamalar için en iyi yaklaşım

useFetch/useAsyncData: otomatik hidrasyon ve önbellek yönetimi

routeRules: her rotanın davranışı için ince ayarlı yapılandırma

Önbellek: üretim performansını optimize etmek için çoklu stratejiler

Hibrit render'ı iyi düşünülmüş bir önbellek stratejisiyle birleştirmek, Single Page Applications'ın etkileşimini koruyarak SEO için optimize edilmiş yüksek performanslı uygulamalar inşa etmeyi sağlar.

Etiketler

#nuxt 3
#vue.js
#ssr
#statik üretim
#web performansı

Paylaş

İlgili makaleler