三种渲染模式与 Nuxt 文件路由
Nuxt 3 与 SSR 概念 · 阅读后完成下方实验与检查题,再用「标记完成」记录进度。
学习目标
Vue 本身只负责「渲染到 DOM」,页面从哪来、首屏 HTML 谁生成,是框架层的问题。这一节讲清 CSR / SSR / SSG 三种模式的取舍,并认识 Nuxt 3 的文件路由——不用手写路由表,目录结构就是路由。
问题场景
你做的 Vue SPA 上线后,分享链接到社交平台是一个白色方块(没有预览图),搜索引擎收录的页面只有空壳;用户在网络慢时先看到白屏再看到内容。这三个问题都是 CSR(客户端渲染)的代价:HTML 在浏览器里才生成。内容型站点需要 SSR 或 SSG。
核心解释
三种模式的本质区别是「HTML 在哪里、什么时候生成」:
| 模式 | HTML 生成时机 | 首屏 | SEO | 适用 |
|---|---|---|---|---|
| CSR | 浏览器执行 JS 后 | 慢(等 JS) | 差(爬虫看到空壳) | 后台系统、强交互应用 |
| SSR | 每次请求时服务端生成 | 快(直接给 HTML) | 好 | 内容站、电商、需要登录态的页面 |
| SSG | 构建时预生成 | 最快(CDN 直接给) | 好 | 博客、文档、营销页 |
Nuxt 3 的核心价值:一套代码同时支持三种模式(ssr: true + 路由规则决定每个页面用哪种),并且把「服务端渲染 → 客户端 hydrate」的细节封装好了。对比 Next.js 的 App Router,Nuxt 的文件约定更简单:pages/ 目录下的每个 .vue 文件就是一条路由。
文件路由:
pages/
├── index.vue → /
├── about.vue → /about
├── courses/
│ ├── index.vue → /courses
│ └── [slug].vue → /courses/:slug
└── [...slug].vue → 404 兜底
[slug].vue 里的参数用 useRoute().params.slug 取;<NuxtLink to="/courses"> 是 Nuxt 版的 RouterLink;<NuxtPage /> 在 app.vue 里作为出口。
nuxt.config.ts 的关键配置:
export default defineNuxtConfig({
ssr: true, // false 则退化为纯 SPA
nitro: {
prerender: {
routes: ["/", "/about"], // SSG:构建时预渲染
},
},
routeRules: {
"/admin/**": { ssr: false }, // 后台页面走 CSR
"/courses/**": { isr: 60 }, // 增量静态再生(60 秒)
},
});
routeRules 是 Nuxt 3 最实用的能力:同一个应用里,营销页 SSG、课程页 SSR、后台 CSR,按路由粒度配置。
示例拆解
一个最小 Nuxt 应用只需要 app.vue、nuxt.config.ts 与 pages/ 目录:app.vue 放导航与 <NuxtPage /> 出口,pages/index.vue 是首页,pages/courses/[slug].vue 是详情页,其余目录按需添加。
<!-- app.vue:导航 + 页面出口 -->
<template>
<div>
<nav>
<NuxtLink to="/">首页</NuxtLink>
</nav>
<NuxtPage />
</div>
</template>
<!-- pages/index.vue:首页用 useFetch 拿数据 -->
<script setup>
const { data: courses } = await useFetch("/api/courses");
</script>
<template>
<ul>
<li v-for="c in courses" :key="c.id">
<NuxtLink :to="`/courses/${c.slug}`">{{ c.title }}</NuxtLink>
</li>
</ul>
</template>
<!-- pages/courses/[slug].vue -->
<script setup>
const route = useRoute();
const { data: course, error } = await useFetch(`/api/courses/${route.params.slug}`);
</script>
<template>
<p v-if="error">课程不存在</p>
<article v-else>
<h1>{{ course.title }}</h1>
<p>{{ course.summary }}</p>
</article>
</template>
启动:npx nuxi init my-app && cd my-app && npm install && npm run dev。验证 SSR 是否生效:浏览器「查看网页源代码」(不是 Elements 面板),能看到 <h1> 里的课程标题——说明 HTML 是服务端生成的。
动手任务
- 建一个 Nuxt 项目,写两个页面 + 一条动态路由,用「查看源代码」验证 SSR。
- 把
ssr改成false再对比源码,记录差异。 - 给
/about配prerender,npm run build后检查.output/public/about/index.html是否在构建时生成。
常见错误
- 在 setup 顶层直接访问
window:SSR 时没有 window,构建或首屏报错。放onMounted或用import.meta.client判断。 - 用
fetch而不是useFetch:数据只在客户端拿,首屏 HTML 没有内容,SEO 白做。 - 以为 Nuxt 只做 SSG:
ssr: true默认就是每请求 SSR;SSG 需要显式 prerender。 - 动态路由文件命名写成
courses/_slug.vue:Nuxt 2 的写法,Nuxt 3 用[slug].vue。
检查与复盘
- 能说清 CSR/SSR/SSG 的 HTML 生成时机与适用场景
- 能写出 Nuxt 的文件路由与动态参数页面
- 能用「查看源代码」验证 SSR 是否生效
参考资料
- Nuxt 官方文档「介绍」:安装与目录结构
- Vue 官方文档「服务端渲染」:SSR 的原理与 hydrate
读完这一课了?
标记完成后,工作台会安排接下来的学习。