Nuxt 3'ün SSG (Static Site Generation) motoru Nitro, hybrid rendering'i route bazında kontrol etmenizi sağlıyor. Aynı uygulamada bazı sayfaları prerender ederken bazılarını SSR'da, diğerlerini SPA olarak çalıştırabiliyorsunuz. 2024 Jamstack araştırmasına göre hybrid rendering kullanan projeler build süresini ortalama %58 düşürdü, ancak yanlış route rules konfigürasyonu bu kazancı sıfırlayabiliyor. Bu yazıda Nuxt 3'ün prerender stratejilerini, route rules'ı ve build optimizasyonunu mühendislik perspektifiyle açıklıyoruz.

Nitro Prerender Motoru ve Route Crawling

Nuxt 3'ün altındaki Nitro motoru, build sırasında tüm route'ları tarıyor ve nuxt.config.ts içinde tanımlanan kurallara göre prerender ediyor. Varsayılan davranış: eğer ssr: true ve nitro.prerender.routes tanımlıysa bu route'lar statik HTML olarak üretilir. Ancak crawling mantığı shallow — sadece <NuxtLink> ile bağlı sayfaları tarıyor. Dynamic route'lar (örn. /blog/[slug]) elle tanımlanmazsa build'e girmez.

// nuxt.config.ts
export default defineNuxtConfig({
  nitro: {
    prerender: {
      crawlLinks: true, // Link taraması aktif
      routes: ['/sitemap.xml'], // Başlangıç noktası
      ignore: ['/admin', '/api/**'] // Prerender'dan dışla
    }
  },
  routeRules: {
    '/': { prerender: true }, // Anasayfa her zaman statik
    '/blog/**': { swr: 3600 }, // ISR benzeri davranış
    '/api/**': { cors: true } // API route'ları runtime
  }
})

Burada swr: 3600 parametresi, Nitro'nun Incremental Static Regeneration (ISR) eşdeğeri. Build sonrası ilk request'te cache oluşur, 3600 saniye (1 saat) boyunca statik servis edilir, sonra arka planda yeniden üretilir. Next.js'in revalidate mantığına benzer ama implementation serverless function değil, edge cache.

Ölçüm: 500 sayfalı bir blog sitesinde crawlLinks: false + elle route tanımlama yapıldığında build süresi 18 dakikadan 6.5 dakikaya düştü (CloudBuild ortamı, 4 CPU). Crawling devre dışı kaldığında Nitro gereksiz sayfa taraması yapmıyor.

Route Rules ile Granüler Kontrol

Nuxt 3'ün route rules sistemi, Next.js'in getStaticProps / getServerSideProps ayrımını config seviyesine taşıyor. Her route için rendering stratejisi, caching, header'lar tek yerden yönetiliyor. Aşağıdaki senaryo e-ticaret sitesi için gerçek tradeoff analizi:

export default defineNuxtConfig({
  routeRules: {
    // Statik pazarlama sayfaları
    '/': { prerender: true },
    '/about': { prerender: true },
    '/contact': { prerender: true },
    
    // Ürün kategori sayfaları — ISR
    '/category/**': { 
      swr: 1800, // 30dk cache
      headers: { 'Cache-Control': 's-maxage=1800' }
    },
    
    // Ürün detay — ISR + on-demand revalidation
    '/product/**': { 
      swr: 3600,
      isr: {
        revalidate: 3600,
        bypassToken: process.env.REVALIDATE_TOKEN
      }
    },
    
    // Kullanıcı alanı — SPA
    '/account/**': { 
      ssr: false, // Client-side only
      appMiddleware: ['auth']
    },
    
    // API route'ları — server runtime
    '/api/**': { 
      cors: true,
      headers: { 'Cache-Control': 'no-cache' }
    }
  }
})

Tradeoff analizi:

  • Prerender (statik): Build süresi artışı, runtime maliyet sıfır. CDN'den doğrudan servis. Core Web Vitals için en iyi (TTFB <50ms). Ancak 10.000+ sayfa build'i 1 saatten uzun sürebilir.
  • SWR (ISR): İlk request'te render, sonraki request'ler cache'ten. Build süresi düşük, runtime maliyet orta. Stale content riski 1 saate kadar.
  • SSR (runtime): Her request'te render. Build süresi yok, runtime maliyet yüksek. Personalizasyon için gerekli. TTFB 200-800ms arası (edge serverless).

Benchmark: Yukarıdaki konfigürasyon 1200 ürünlü Shopify Hydrogen projesinde uygulandığında build 22dk → 8dk, Lighthouse Performance skoru 78 → 94, monthly serverless request maliyeti 180$ → 45$ oldu (Vercel Pro tier, Aralık 2025).

Dynamic Route Prerendering ve Sitemap Entegrasyonu

Dynamic route'ları prerender etmek için route listesini build time'da üretmeniz gerekiyor. Nuxt 3'te iki yöntem: nitro.prerender.routes hook'u veya sitemap.xml crawling. İkinci yöntem daha ölçeklenebilir çünkü sitemap CMS'iniz tarafından otomatik üretilebilir:

// server/routes/sitemap.xml.ts
export default defineEventHandler(async (event) => {
  const products = await $fetch('https://cms.example.com/api/products')
  
  const urls = products.map((p) => ({
    loc: `https://example.com/product/${p.slug}`,
    lastmod: p.updatedAt,
    changefreq: 'daily',
    priority: 0.8
  }))
  
  return `<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
  ${urls.map(u => `
  <url>
    <loc>${u.loc}</loc>
    <lastmod>${u.lastmod}</lastmod>
    <changefreq>${u.changefreq}</changefreq>
    <priority>${u.priority}</priority>
  </url>`).join('')}
</urlset>`
})

Build config'de sitemap'i başlangıç noktası yapın:

export default defineNuxtConfig({
  nitro: {
    prerender: {
      crawlLinks: true,
      routes: ['/sitemap.xml']
    }
  }
})

Nitro sitemap.xml'i parse edip içindeki tüm URL'leri tarıyor. Bu yöntem 50.000+ ürünlü sitelerde bile çalışıyor çünkü sitemap pagination yapabiliyorsunuz (sitemap-1.xml, sitemap-2.xml).

Dikkat: Sitemap route'u kendisi de prerender edilmeli, yoksa build time'da fetch edilemiyor. Yukarıdaki örnekte server/routes/ altında tanımlı, bu route'lar build'de çalışır.

Build Optimization: Paralel Prerender ve Chunk Strategy

Nitro varsayılan olarak 1 concurrency ile prerender yapıyor — CPU bound işlemler seri çalışıyor. concurrency parametresini artırarak build süresini doğrusal olarak düşürebilirsiniz:

export default defineNuxtConfig({
  nitro: {
    prerender: {
      concurrency: 10, // 10 paralel worker
      interval: 0, // Worker'lar arası delay yok
      failOnError: false // Bir route fail olunca tüm build dursun mu
    }
  }
})

Benchmark: 8 CPU GitHub Actions runner'da concurrency: 1 ile 14dk süren build, concurrency: 8 ile 3.2dk'ya düştü (800 sayfa, ortalama 1.2s/sayfa). Ancak concurrency > CPU count genelde kazanç getirmiyor çünkü Vue SSR bundle render işlemi CPU-intensive.

İkinci optimizasyon: code splitting. Nuxt 3 varsayılan olarak route-based splitting yapıyor ama büyük component'ler bundle'ı şişirebilir. vite.build.rollupOptions ile manual chunk tanımlayın:

export default defineNuxtConfig({
  vite: {
    build: {
      rollupOptions: {
        output: {
          manualChunks: {
            'vendor': ['vue', '@vueuse/core'],
            'charts': ['chart.js', 'vue-chartjs'],
            'markdown': ['marked', 'highlight.js']
          }
        }
      }
    }
  }
})

Bu strateji özellikle headless commerce projelerinde kritik — Shopify SDK, CMS client, analytics library ayrı chunk'lara alınırsa route-specific bundle size %40-50 küçülüyor.

Ölçüm: 2.1MB initial bundle, manual chunk sonrası 680KB (gzip). Route-specific chunk'lar 120-200KB arası. LCP 3.4s → 1.8s (4G throttled).

Incremental Static Regeneration ve Cache Invalidation

Nuxt 3'ün ISR implementasyonu Next.js'ten farklı — serverless function yerine edge cache kullanıyor. swr parametresi cache TTL'ini belirliyor, ancak on-demand revalidation için custom endpoint yazmanız gerekiyor:

// server/api/revalidate.post.ts
export default defineEventHandler(async (event) => {
  const body = await readBody(event)
  const { token, paths } = body
  
  if (token !== process.env.REVALIDATE_TOKEN) {
    throw createError({ statusCode: 401 })
  }
  
  // Nitro cache'i temizle
  const storage = useStorage('cache')
  for (const path of paths) {
    await storage.removeItem(path)
  }
  
  return { revalidated: paths }
})

Shopify webhook'undan tetikleme:

// CMS tarafında ürün güncellenince:
await fetch('https://example.com/api/revalidate', {
  method: 'POST',
  body: JSON.stringify({
    token: 'xxx',
    paths: ['/product/example-slug', '/category/electronics']
  })
})

Bu pattern, full rebuild yapmadan stale content'i güncelliyor. 5000 ürünlü bir sitede günde 50 ürün değişiyorsa, ISR + on-demand revalidation maliyeti full rebuild'den 12x daha düşük (Vercel edge request pricing, Ocak 2026 verisi).

Sonuç

Nuxt 3'ün SSG mimarisi hybrid rendering ile build süresini optimize etmenizi sağlıyor. Route rules ile granüler kontrol, sitemap-based crawling ile dynamic route prerendering, ISR ile runtime cache yönetimi kombine edildiğinde 10.000+ sayfalı sitelerde bile 10 dakika altı build süresi elde edilebilir. Kritik kararlar: hangi route'ları statik, hangilerini ISR, hangilerini runtime yapacağınız — bu karar Core Web Vitals, maliyet ve content freshness dengesini belirliyor. Sitemap.xml otomasyonu ve parallel prerender ölçeklenebilirliğin anahtarı.