Advanced Nuxt 3 - Deep Dive into SSR and SSG
Nuxt 3 represents the pinnacle of full-stack frameworks in the Vue ecosystem. Its brand-new Nitro engine unifies server-side and build-time rendering logic, making switching between SSR, SSG, and ISR simpler than ever before.
Essential Differences Between Three Rendering Modes
To understand how Nitro works, first clarify how the three rendering modes differ in their request lifecycle:
<script setup lang="ts">
import { useAsyncData, useFetch, definePageMeta } from '#app'
definePageMeta({
name: 'products-id',
path: '/products/:id',
middleware: ['auth'],
layout: 'shop'
})
const route = useRoute()
const { data: product, pending, refresh, error } = await useAsyncData(
`product-${route.params.id}`,
() => $fetch(`/api/products/${route.params.id}`),
{
server: true,
lazy: false,
default: () => ({}),
transform: (res) => ({
...res,
formattedPrice: new Intl.NumberFormat('zh-CN', {
style: 'currency',
currency: 'CNY'
}).format(res.price)
})
}
)
const { data: related } = await useFetch(
'/api/products/related',
{
params: { id: route.params.id, limit: 4 },
pick: ['items']
}
)
useHead({
title: () => product.value?.name,
meta: [
{ name: 'description', content: () => product.value?.description }
]
})
const isrTimestamp = ref(Date.now())
</script>
<template>
<div class="product-page">
<Suspense>
<template #default>
<ProductDetail v-if="product" :product="product" />
<div v-else-if="error" class="error">{{ error.message }}</div>
</template>
<template #fallback>
<ProductSkeleton />
</template>
</Suspense>
<RelatedProducts v-if="related" :items="related.items" />
</div>
</template>
Performance and Cost Comparison
| Render Mode | First TTFB | Time to Interactive | Server Cost | SEO Friendly | Best For |
|---|---|---|---|---|---|
| SSR | ⚡ 300-600ms | ✅ Excellent | 💸 High | ✅ Best | Personalized, realtime data |
| SSG | ⚡⚡⚡ <50ms | ✅ Best | 🆓 Very Low | ✅ Best | Docs, blogs, product pages |
| ISR | ⚡⚡ <100ms* | ✅ Excellent | 💰 Medium | ✅ Good | E-commerce, news, mid-freq updates |
| CSR (SPA) | 🐢 200ms + JS | ❌ Poor | 🆓 Very Low | ⚠️ Extra work needed | Dashboards, admin panels |
*After first visit, cache hit scenario
Cache Strategy Configuration
Nitro provides multi-layer caching mechanisms. For SSR pages, configure CDN-level caching via routeRules:
// nuxt.config.ts
export default defineNuxtConfig({
routeRules: {
'/products/**': { swr: 3600 },
'/api/**': { swr: 60, cache: { maxItems: 1000 } },
'/static/**': { static: true }
}
})
SWR (Stale-While-Revalidate) mode returns cached responses immediately while refreshing data in the background, balancing performance and data freshness.
Hydration Optimization Techniques
In SSR mode, the client needs to hydrate server-rendered HTML. Common performance pitfalls include:
- Payload too large: Use
definePageMeta'spayloadExtractorto slim down data sent to the client - Premature hydration: Wrap non-critical interactive components with
<ClientOnly> - Duplicate requests: Properly set useAsyncData keys; leverage Nitro's automatic request deduping
Best Practices
- Default to SSG unless pages strongly depend on real-time or personalized data
- Mix rendering modes: Home SSG + Detail ISR + User Center SSR rather than uniform approach
- Leverage Nitro API Routes to avoid separate backend deployments (for small/medium projects)
- Monitor TTFB distribution — if P95 > 1s, consider enabling cache or switching to ISR
- Use
nuxt build --prerenderto pre-render high-frequency pages, reducing cold-start time
Summary
Nuxt 3's Nitro engine completely erases the development differences between SSR and SSG. Developers focus purely on business logic, switching between rendering modes through configuration. The key to choosing a rendering mode lies in: page update frequency × content personalization degree × team ops capability.