SSG 静态站点生成与预渲染
不是所有页面都需要每次请求都渲染——营销落地页、博客文章、帮助文档这些内容变化不频繁的页面,在构建时生成静态 HTML 就够了。SSG 将 SSR 的能力前移到构建阶段,产出纯静态文件,可以部署到任何 CDN,零服务器成本、极致速度。本章讲清 Nuxt4 的 SSG 工作流、预渲染策略、ISR 增量再生,以及 AI 视频落地页的 SEO 实战。
1. nuxi generate 工作流
1.1 SSG 的核心思路
构建时(Build Time) 运行时(Runtime)
───────────────────── ──────────────────
nuxi generate CDN / Static Hosting
↓ ↓
① 构建应用 用户请求 /about
② 爬取所有路由 ↓
③ 逐个路由执行 SSR 直接返回 about.html
④ 输出 .output/public/ (无需服务器、零延迟)
├── index.html
├── about.html
├── videos/index.html
└── _nuxt/ (JS/CSS)
1.2 使用 nuxi generate
# 生成静态站点
npx nuxi generate
# 等价于
npx nuxi build --prerender输出目录:.output/public/——所有 HTML、JS、CSS 都是纯静态文件。
1.3 SSG vs SSR 对比
| 特性 | SSG (nuxi generate) | SSR (nuxi build) |
|---|---|---|
| 渲染时机 | 构建时 | 每次请求时 |
| 部署要求 | 静态托管(CDN、GitHub Pages) | 需要 Node.js 服务器 |
| 首屏速度 | 极快(直接返回 HTML) | 快(服务端渲染后返回) |
| 数据新鲜度 | 构建时的快照 | 实时 |
| 服务器成本 | 零 | 有(CPU + 内存) |
| 适用场景 | 博客、文档、落地页 | 动态内容、用户个性化页面 |
2. routeRules prerender 配置
2.1 在 nuxt.config.ts 中配置预渲染
// nuxt.config.ts
export default defineNuxtConfig({
routeRules: {
// 指定路由在构建时预渲染
'/': { prerender: true },
'/about': { prerender: true },
'/pricing': { prerender: true },
'/blog/**': { prerender: true }, // 通配符:所有博客页面
// 排除某些路由不预渲染
'/studio/**': { prerender: false }, // 工作台不需要预渲染
'/admin/**': { prerender: false }, // 管理后台不需要预渲染
},
})2.2 使用 definePageMeta 标记
也可以在页面组件中标记是否预渲染:
<!-- app/pages/about.vue -->
<script setup lang="ts">
defineRouteRules({
prerender: true, // 构建时预渲染此页面
})
</script>2.3 nuxt.config 中的 nitro.prerender 配置
// nuxt.config.ts
export default defineNuxtConfig({
nitro: {
prerender: {
// 入口路由(爬虫从这些页面开始)
routes: ['/', '/sitemap.xml'],
// 额外爬取这些路由
crawlLinks: true, // 自动发现 <a> 链接
// 忽略某些路由
ignore: [
'/api/**', // 不预渲染 API 路由
'/studio/**', // 不预渲染工作台
],
// 并发数(加快构建速度)
concurrency: 10,
// 失败时不中断构建
failOnError: false,
},
},
})3. 动态路由预渲染爬取策略
3.1 自动爬取(crawlLinks)
当 crawlLinks: true 时,Nuxt 会从入口页面开始,递归发现所有 <a> 和 <NuxtLink> 链接:
入口 /
├── 发现链接 /about → 预渲染 /about
├── 发现链接 /blog → 预渲染 /blog
│ ├── 发现链接 /blog/post-1 → 预渲染 /blog/post-1
│ └── 发现链接 /blog/post-2 → 预渲染 /blog/post-2
└── 发现链接 /videos → 预渲染 /videos
├── 发现链接 /videos/abc → 预渲染 /videos/abc
└── ...
限制:爬虫只能发现 HTML 中静态存在的链接。如果链接是通过 JavaScript 动态生成的(如无限滚动列表),爬虫无法发现。
3.2 手动指定动态路由
对于爬虫无法自动发现的动态路由,手动指定:
// nuxt.config.ts
export default defineNuxtConfig({
nitro: {
prerender: {
routes: [
'/',
'/videos/abc123',
'/videos/def456',
'/videos/ghi789',
],
},
},
})3.3 从 API 动态获取路由列表
更优雅的方式是在构建时从 API 获取所有需要预渲染的路由:
// nuxt.config.ts
export default defineNuxtConfig({
hooks: {
async 'prerender:routes'(ctx) {
// 从 API 获取所有视频 ID
const videos = await fetch('https://api.example.com/videos?fields=id')
.then(res => res.json())
// 添加到预渲染列表
for (const video of videos) {
ctx.routes.add(`/videos/${video.id}`)
}
// 从 CMS 获取所有博客文章
const posts = await fetch('https://cms.example.com/posts?fields=slug')
.then(res => res.json())
for (const post of posts) {
ctx.routes.add(`/blog/${post.slug}`)
}
},
},
})3.4 生成 sitemap.xml
配合 @nuxtjs/sitemap 模块自动生成站点地图:
// nuxt.config.ts
export default defineNuxtConfig({
modules: ['@nuxtjs/sitemap'],
site: { url: 'https://aivideo.example.com' },
sitemap: {
// 动态路由通过 API 获取
sources: ['/api/__sitemap__/urls'],
},
})// server/api/__sitemap__/urls.ts
export default defineSitemapEventHandler(async () => {
const videos = await fetchVideoList()
return videos.map(v => ({
loc: `/videos/${v.id}`,
lastmod: v.updatedAt,
changefreq: 'weekly',
priority: 0.8,
}))
})4. 增量静态再生(ISR)原理
4.1 ISR 解决了什么问题
SSG 的问题:数据在构建时就固定了,内容更新需要重新构建整个站点。
ISR(Incremental Static Regeneration)的方案:
首次请求 /videos/123
→ 缓存中没有 → 服务端 SSR → 返回 HTML → 缓存 HTML(TTL = 60s)
60 秒内的后续请求
→ 缓存命中 → 直接返回缓存的 HTML(极快)
60 秒后的请求
→ 缓存过期 → 返回旧缓存(stale) → 后台异步重新渲染 → 更新缓存
4.2 在 Nuxt4 中配置 ISR
// nuxt.config.ts
export default defineNuxtConfig({
routeRules: {
// ISR:缓存 60 秒后自动刷新
'/videos/**': {
isr: 60, // 60 秒后重新验证
},
// ISR:缓存 1 小时
'/blog/**': {
isr: 3600,
},
// 永久缓存(等同于 SSG,但按需生成)
'/docs/**': {
isr: true, // 永不过期
},
// SWR 模式:立即返回缓存,后台刷新
'/categories/**': {
swr: 300, // 缓存 5 分钟
},
},
})4.3 ISR vs SSG vs SSR
| 特性 | SSG | ISR | SSR |
|---|---|---|---|
| 渲染时机 | 构建时 | 首次请求时 + 定期刷新 | 每次请求 |
| 数据新鲜度 | 构建时快照 | 近实时(TTL 控制) | 实时 |
| 响应速度 | 极快(静态文件) | 极快(缓存命中时) | 一般 |
| 服务器要求 | 无 | 有(但负载很低) | 有 |
| 新增内容 | 需要重新构建 | 自动按需生成 | 自动 |
4.4 ISR 部署要求
ISR 需要一个支持缓存的运行时环境:
- Vercel:原生支持
- Netlify:通过 Edge Functions 支持
- Cloudflare Workers:通过 KV 存储实现
- 自建服务器:需要配置缓存层(Redis / 文件缓存)
5. AI 视频落地页 SSG + SEO 实战
5.1 页面规划
需要 SSG 的页面(内容稳定、SEO 重要):
├── / ← 首页落地页
├── /about ← 关于我们
├── /pricing ← 定价页
├── /features ← 功能介绍
├── /blog/** ← 博客文章
└── /help/** ← 帮助文档
需要 ISR 的页面(内容会更新、SEO 重要):
├── /videos/** ← 视频详情页(ISR 60s)
└── /categories/** ← 分类页(ISR 300s)
需要 SSR 的页面(个性化、实时数据):
├── /studio/** ← 创作工作台
├── /admin/** ← 管理后台
└── /search ← 搜索结果页
5.2 完整 routeRules 配置
// nuxt.config.ts
export default defineNuxtConfig({
routeRules: {
// SSG:构建时预渲染
'/': { prerender: true },
'/about': { prerender: true },
'/pricing': { prerender: true },
'/features': { prerender: true },
'/blog/**': { prerender: true },
'/help/**': { prerender: true },
// ISR:缓存 + 定期刷新
'/videos/**': { isr: 60 },
'/categories/**': { swr: 300 },
// SSR:每次请求实时渲染
'/studio/**': { ssr: true },
'/admin/**': { ssr: true },
// SPA:客户端渲染(不需要 SEO 的页面)
'/settings/**': { ssr: false },
},
})5.3 SEO 优化:useHead + useSeoMeta
<!-- app/pages/videos/[id].vue -->
<script setup lang="ts">
const route = useRoute()
const { data: video } = await useFetch(`/api/videos/${route.params.id}`)
if (!video.value) {
throw createError({ statusCode: 404, message: '视频不存在' })
}
// SEO 元信息
useSeoMeta({
title: `${video.value.title} - AI 视频平台`,
description: video.value.description?.slice(0, 160),
ogTitle: video.value.title,
ogDescription: video.value.description?.slice(0, 160),
ogImage: video.value.thumbnail,
ogType: 'video.other',
twitterCard: 'summary_large_image',
})
// 结构化数据(JSON-LD)
useHead({
script: [{
type: 'application/ld+json',
innerHTML: JSON.stringify({
'@context': 'https://schema.org',
'@type': 'VideoObject',
name: video.value.title,
description: video.value.description,
thumbnailUrl: video.value.thumbnail,
uploadDate: video.value.createdAt,
duration: `PT${video.value.duration}S`,
contentUrl: video.value.url,
}),
}],
})
</script>5.4 落地页 SSG 示例
<!-- app/pages/index.vue — 首页落地页 -->
<script setup lang="ts">
defineRouteRules({ prerender: true })
useSeoMeta({
title: 'AI 视频平台 - 用 AI 创造精彩视频',
description: '基于最新 AI 技术,一键生成、编辑、发布高质量视频内容。',
ogImage: '/images/og-home.jpg',
})
// 构建时获取精选视频(数据会嵌入静态 HTML)
const { data: featured } = await useFetch('/api/videos/featured', {
pick: ['id', 'title', 'thumbnail', 'views'],
})
</script>
<template>
<div>
<HeroSection />
<FeaturedVideos :videos="featured" />
<FeatureShowcase />
<PricingTable />
<CTASection />
</div>
</template>本章小结
- nuxi generate:构建时预渲染所有页面,输出纯静态 HTML
- routeRules prerender:精确控制哪些路由需要预渲染
- crawlLinks:自动爬取
<a>链接发现路由;动态路由需手动指定或从 API 获取 - ISR:首次请求渲染 + 缓存 + 定期刷新,兼顾性能和数据新鲜度
- SEO:
useSeoMeta设置 OG 标签,useHead注入 JSON-LD 结构化数据 - 实战:落地页用 SSG、视频详情用 ISR、工作台用 SSR——按场景选择最佳渲染策略
下一章将这三种渲染模式统一起来——混合渲染架构设计。