☰
Next.js与Nuxt.js静态生成深度对比:原理、配置与选型指南
2026/10/6 17:18:36 网站建设 项目流程

静态站点生成这事,圈子里讨论好几年了,但最近几个月明显又热了起来。一方面是各家云厂商的静态托管越来越便宜,另一方面是 Next.js 和 Nuxt.js 把 SSG 的体验做得越来越顺滑,以至于不少人开始重新审视:是不是可以放弃传统服务器渲染,直接用纯静态方案搞定大部分项目?

这里说的 SSG(Static Site Generation),核心思路是在构建阶段就把页面 HTML 生成好,而不是等用户请求来了再现场渲染。这样做的直接好处是部署简单、响应快、成本低,也不容易被动态渲染拖垮。而 Next.js 和 Nuxt.js 这两个框架,分别站在 React 和 Vue 生态里,把 SSG 从“上古时代的 Jekyll/Hugo 式模板”推进到了“带数据、带路由、带组件体系”的现代开发模式。

这篇文章我不打算写那种“A 框架好还是 B 框架好”的二极管结论,而是把两条路线的实现原理、实操配置、典型的坑,以及我自己的选型经验都铺开聊一聊。无论你是准备给团队做技术调研,还是自己私下想折腾一个博客或文档站,应该都能从中找到可以参考的东西。

1. 重新认识:SSG 到底解决什么问题

很多人一提 SSG,第一反应就是“博客”。确实,博客是 SSG 最传统的应用场景,但它能做的远不止这些。我劝你先把“SSG 等于博客”这个刻板印象丢掉,否则会错失很多优化思路。

1.1 SSG 的独特优势和边界

SSG 最本质的特点是:内容在构建时固定,运行时不再动态生成。打个比方,传统的服务端渲染就像是餐厅现点现做,你下单了厨房才开始动火;而 SSG 就像是提前做好的一批盒饭,客人来了直接拿走,连加热都省了。这个比喻背后藏着几个实打实的好处:

  • 响应速度极快。生成出来的 HTML 是纯静态文件,CDN 可以直接缓存到节点上,用户访问时基本就是“文件读取 + 网络传输”,没有数据库查询、没有模板渲染、没有进程间通信。
  • 部署极其简单。你不需要在服务器上装 Node.js 运行时(除非用了 ISR 或 SSR 这类动态能力),一个 Nginx、一个对象存储、甚至 GitHub Pages 都能跑。
  • 成本可以压到很低。静态文件托管几乎是最便宜的托管方式,个人项目可以做到零成本,商业项目也能省掉大量服务器开销。
  • 天然抗流量冲击。因为是静态文件,流量再怎么涨,CDN 都能扛,不存在“并发过高把服务打崩”的场景。

但 SSG 的边界也很明显:凡是需要“千人千面”的内容,它都搞不定。比如用户登录后的个性化页面、实时库存、评论区动态刷新,这些都不能靠纯构建时静态化来解决。不是说不能用 SSG,而是需要额外接客户端数据请求、或者混合渲染方案来弥补。

我在实际项目中判断是否用 SSG,就看一条:这个页面的核心内容,是不是对所有用户都一样?如果答案是“绝大部分一样,只有少量个人化信息”,那就可以用 SSG + 客户端增强的混合模式。如果页面完全依赖用户身份来生成,那 SSG 就不是第一选择了。

1.2 为什么现在值得关注 SSG

其实 SSG 不是新东西,2015 年左右 Jekyll、Hexo 那一波就很火。但现在的 SSG 和那会儿完全是两个物种,变化主要有三点:

第一,组件化开发模式。过去的静态站生成器靠的是模板和 Markdown,写点复杂交互非常痛苦。而 Next.js 和 Nuxt.js 本身就是完整的应用框架,组件、状态管理、路由、样式方案一应俱全。你是在“写一个应用”,只是顺带把它导出成了静态文件。

第二,混合渲染能力。这两个框架都支持同一个项目里既有 SSG 页面,也有按需 SSR 的页面,甚至可以细粒度到某个路由的某部分用静态、另一部分用动态。自由度比过去高太多。

第三,生态和工具链成熟。图片优化、代码分割、预加载、SEO 标签管理,这些在过去需要自己拼装的件,现在框架都内置了。特别是 Next.js 的 Image 组件和 Nuxt 的 NuxtImg 模块,对静态站点的性能优化帮助非常大。

所以我的观点是:SSG 不是“复古”的工程手段,而是现代前端架构里性价比极高的一档选项。关键看你选哪条技术路线,以及怎么控制好细节。

2. Next.js 的静态生成:路线与细节

Next.js 从很早开始就把静态生成作为核心卖点之一,这也是它当初能在 React 生态里杀出来的重要原因。我对它的使用经历了从 Pages Router 到 App Router 的完整过渡,下面重点讲现在的主流做法。

2.1 从 Pages Router 到 App Router 的静态化路径

Pages Router 时代的静态生成很直白:你导出一个getStaticProps,框架就会在构建时执行这个函数,把返回的数据塞进页面 props 里,然后渲染成 HTML。动态路由配合getStaticPaths,可以预先列出所有需要生成的路径。这个模式简单好理解,直到现在还有大量项目在用。

App Router 上线后,静态化的思路变了。它不再有getStaticProps这个魔法函数,而是改为:默认情况下,只要不调用动态 API(比如cookies()、headers()、searchParams的读取),页面就会被视为静态,构建时自动生成。配合generateStaticParams函数来声明动态路由需要预渲染的参数列表,整体更加内敛、自动化。

我自己用 App Router 的实际感受是:“默认静态”的设计把心智负担降得很低。你不需要刻意考虑“这个页面要不要静态化”,只要别乱用动态 API,框架自动帮你物尽其用。这也符合 Next.js 近几年的设计理念——用约定优于配置,让正确的事情更容易发生。

但这里有个点必须提醒:App Router 里的“静态”分两层。第一层是静态预渲染(构建时就生成 HTML),第二层是静态缓存(第一次请求后缓存起来,之后复用)。后者更像过去的 ISR 或 SWR 的概念,不要混淆。如果你想做纯静态导出,必须显式打开output: 'export',否则部署环境里可能还是会有 Node 服务在跑。

2.2 关键配置与常用模式

纯静态导出的第一步,是在next.config.ts里加上一句:

import type { NextConfig } from "next"; const nextConfig: NextConfig = { output: "export", // 如果你的静态站部署在一个子路径下,比如 GitHub Pages 的 /repo/, // 需要设置 basePath // basePath: "/my-repo", images: { unoptimized: true, }, }; export default nextConfig;

注意images.unoptimized: true这个配置。Next.js 自带的 Image 组件默认会走它的图片优化服务,但这个服务依赖服务器运行时。静态导出模式下没有服务器,所以要么关掉优化,要么预先用外部工具把图片处理好,否则构建会直接报错。

动态路由的预渲染,核心是generateStaticParams。举个例子,你要生成一个博客站,路径是/posts/[slug]:

export async function generateStaticParams() { const posts = await getAllPosts(); // 自己实现的抓取逻辑 return posts.map((post) => ({ slug: post.slug, })); }

构建时框架会调用这个函数,依次为每个 slug 生成一个 HTML 页面。这里有个容易被忽略的点:generateStaticParams里返回的参数必须是字符串数组(或类似的可序列化结构),不能返回 Promise,也不能依赖动态数据。它更像是一个“构建时的配置清单”,而不是数据请求层。

App Router 里抓数据的函数,比如generateMetadata和页面组件里请求的数据,默认会在构建时执行一遍。但有一点要特别留意:如果你的页面组件里使用了只在客户端生效的交互组件,记得用"use client"单独标记,否则构建时会尝试在服务器环境跑这些组件,很容易出问题。

我的一个习惯性做法是:页面主体组件保持服务器组件(Server Component),把需要交互的部分(弹窗、点赞按钮、筛选器)拆成独立的客户端组件。这样静态生成的 HTML 里直接就有完整内容,SEO友好,交互也不受影响。

3. Nuxt.js 的静态生成:路线与细节

Nuxt.js 这边,Vue 生态对 SSG 的支持也一直很在线。尤其是 Nuxt 3 之后,基于 Nitro 引擎的预渲染机制比之前版本强了不少。如果你本来就是 Vue 技术栈,这条路值得认真看。

3.1 生成流程与预渲染机制

Nuxt 3 的静态生成,本质上是对 Nitro 服务器能力的发挥。你用nuxi build构建时,Nitro 会生成一个可以独立运行的服务端程序;而当你执行nuxi generate时,Nitro 会在构建完成后自动爬取所有路由,把每个页面渲染成静态 HTML 并输出到.output/public目录。

这里有个关键的判断点:Nuxt 的静态生成不是你手动声明“要生成哪些页面”,而是框架自己去“发现”页面。你定义好路由结构,再写好数据抓取逻辑,nuxi generate会自动遍历所有可静态化的路径。对于动态路由,比如/posts/[id].vue,你需要在generate配置或者useFetch的钩子里提供可能的参数列表。

// nuxt.config.ts export default defineNuxtConfig({ nitro: { prerender: { routes: ["/", "/about"], crawlLinks: true, }, }, });

crawlLinks: true是默认开启的,它会顺着页面里出现的链接继续爬取,这既是好事也是坑:好处是你不用手动把每条路由都写进配置;坏处是一旦有个链接指向了外部的 API 或者不存在的路由,构建过程可能卡住或者报错。我的经验是:保持crawlLinks开启,但把第三方外链都变成纯链接标签(不对应实际路由的那些),避免被框架误爬。

页面数据抓取方面,Nuxt 提供了一套非常顺滑的配套:

<script setup lang="ts"> const { data: post } = await useAsyncData("post", () => $fetch(`/api/posts/${route.params.id}`) ); </script>

useAsyncData配合$fetch在构建时会真实执行一次请求,数据会被序列化到页面 payload 里。这意味着:如果你把生成站点的服务器地址写在环境变量里,构建机的网络一定要能访问到它。很多人在自己的电脑上构建没问题,一到 CI 就报错,基本都是这个原因。

3.2 构建时数据抓取与 payload 优化

Nuxt 静态生成后,每个页面都会携带一个内联的 payload(数据快照),用于客户端激活时恢复组件状态。这个机制让页面在静态 HTML 和客户端交互之间保持数据一致,用户体验很好,但也带来了一个隐患:payload 太大,页面首屏脚本体积会明显膨胀。

所以我一直建议关注.output/public/_payload目录里生成的 JSON 文件大小。如果你的页面请求了一个巨大的列表,或者后端接口返回了一堆用不到的字段,这些数据都会被原样塞进 payload。可以做的优化有两个方向:

一是在服务端做数据裁剪。请求时只取前端真正需要的字段,比如用$fetch的query参数或后端接口的分页能力。

二是按需延迟加载。有些数据不需要在首屏出现,可以放到客户端组件里用useFetch按需请求,而不是一开始就await。这样静态生成的 HTML 里不带这部分数据 payload,体积就小多了。

我在一个文档项目里实测过,某页面原来的 payload 有 2MB,裁剪字段 + 延迟加载后降到 200KB,构建体积和页面加载速度都有明显改善。这算是 Nuxt SSG 项目里性价比最高的优化手段之一。

4. 两者横向对比:关键差异决定选型

聊完各自的技术路线,直接把两个框架放到同一张台面上比一比。这里说的对比不是说谁比谁强,而是在 SSG 这个特定场景下,它们各自的适用位置不同。

4.1 核心差异对比表

下面这个表格是我根据自己的使用经验整理的,基本涵盖了选型时最关心的维度:

对比维度Next.jsNuxt.js
基础生态React/React Server ComponentsVue/Vue Composition API
典型静态配置output: "export"nuxi generate
动态路由预渲染generateStaticParamsprerender.routes+ 自动爬取
数据获取getStaticProps(Pages)/ RSC 构建时解析useAsyncData/useFetch
静态托管适配需注意 Image 组件和 basePath默认输出相对友好
学习曲线中高,尤其 App Router 概念多中低,新手友好
适合的项目中大型团队、复杂组件生态中小型项目、Vue 团队、快速上线

从表格能看出,Next.js 的静态化路径更“工程化”,需要开发者对框架的运行机制有更完整的理解;Nuxt 则更像一个“开箱即用”的解决方案,交给它就能生成一份可用的静态站点,不需要想太多底层机制。

4.2 场景适用性与生态考量

选型这件事,脱离团队背景谈优劣都是耍流氓。

如果团队已经熟练使用 React,或者项目里大量依赖 React 生态的组件库、状态管理方案,那 Next.js 是再自然不过的选择。React 生态的积累太厚了,几乎任何需求都能找到成熟的现成方案。特别是需要复杂交互、富客户端逻辑的项目,React 的组件生态能给到很强的支撑。

反过来,如果团队是 Vue 背景,或者项目本身不算复杂,Nuxt 的体验会非常舒畅。Vue 的单文件组件模式在中小型内容站、博客、文档站这些场景下“写得快、改得快”,配合 Nuxt 的自动导入和模块系统,开发效率确实高。

还有一个经常被忽略的点:团队的长期维护意愿。Next.js 的迭代节奏比较快,App Router 刚出来那会儿,很多习惯了 Pages Router 的开发者都被打了个措手不及。Nuxt 3 相对而言更稳妥,从 Nuxt 2 升级到 Nuxt 3 虽然也有迁移成本,但大方向上 Vue 的核心思想没变过。如果你不希望团队频繁追新,Nuxt 会更省心。

不过我必须说一句:这种对比很容易变成“信仰之争”。我的建议是,如果你的项目两种框架都能做,那就做一个十几页的原型页面,跑通一遍静态生成流程,看看哪个团队上手更快、迭代更顺手。纸面上的特性,永远不如实际跑一遍来得真实。

5. 实操踩坑记录:最常见的几个坑

不管是 Next.js 还是 Nuxt,静态生成在实操中都会遇到一批“看起来怪怪的、报错还不直观”的问题。我把这些年踩过的坑整理成排查清单,希望能帮你省点时间。

5.1 Next.js 静态导出环节的几个坑

第一个坑,也是最容易直接卡死构建的:Image 组件没配置。前面提过,next/image默认走优化服务,静态导出必须加images.unoptimized: true。但即便加了,如果你用了fill或者src传的是外部 URL,构建时依然可能提示无法优化。我的建议是:静态导出的项目,直接用普通的<img>标签或者换成next/legacy/image,别追这个功能。

第二个坑是generateStaticParams返回值不合法。App Router 里这个函数必须返回一个数组,数组元素是普通对象,不能带undefined值。如果你从数据库里读取的字段可能为空,记得先做默认值处理,否则构建时会报序列化错误。

第三个坑比较隐蔽:output: "export"模式下,动态 API 全不可用。凡是用了cookies()、headers()、searchParams的页面,会被强制标记为动态渲染,和静态导出冲突。构建时不一定会立刻报错,但生成的页面可能内容为空,或者直接跳过某些路由。遇到这种情况,要么把页面改成纯客户端数据请求,要么放弃纯静态导出、改用 Node 部署。

5.2 Nuxt 生成环节的几个坑

Nuxt 这边,我遇到最多的坑是构建时外链依赖。项目里的一个按钮链到了某个外部网站,结果crawlLinks把那个外链也当成路由去爬了,构建过程卡了几分钟才超时。解决办法是在链接上加target="_blank"和rel="noopener",或者干脆把外链渲染成纯<a>标签,不让爬虫追踪。

另一个常见的坑是useAsyncData的 key 冲突。Nuxt 里每个数据请求都需要一个唯一的 key,如果两个不同页面的请求用了同一个 key,生成时可能会出现数据串台的情况。给每个请求加上符合语义的前缀,比如post-${id},能有效避免这类问题。

还有一个容易被忽略的:静态站点的NODE_ENV。nuxi generate默认会按开发模式处理某些逻辑,如果你在代码里判断了process.env.NODE_ENV,可能会导致构建时的行为和线上不一致。建议在 CI 环境里显式设置NODE_ENV=production,让生成结果更接近真实发布状态。

6. 选型决策指南:你该用哪个

到这里,两条路线的内部机制和实操细节都过得差不多了。如果你还在犹豫,我提供一个我经常用来帮团队做决策的判断清单,供你参考。

6.1 决策清单

站在项目角度,你可以依次回答下面三个问题:

  1. 团队主要技术栈是什么?这是第一道也是最重要的筛子。React 团队选 Next.js,Vue 团队选 Nuxt.js,强行跨生态不是不行,但学习成本和长期维护成本都会变高。
  2. 项目内容有多大比例是公共内容?如果公共内容占 80% 以上,SSG 是合适的,两个框架都能满足;如果动态内容占比很高,你需要重新评估是否真的适合静态方案。
  3. 团队对框架迭代的接受度怎样?愿意持续跟进生态变化、接受新概念的团队,Next.js 的长期潜力更大;求稳、希望一次写好长期不动的团队,Nuxt 更省心。

6.2 基于团队与项目背景的参考建议

我给你一个相对务实的组合建议:

  • 如果你是个人博主、独立开发者,想做内容站或文档站,又是 Vue 背景,Nuxt.js 是首选,它上手快、生成稳,折腾空间小,能让你把精力放在内容本身。
  • 如果你在电商公司或者团队已经有完整的 React 基础设施,要做官网、营销页、产品文档这类对性能有要求、又有一定交互深度的站点,Next.js 更合适,它的组件生态和优化能力能帮你把站点做到极致。
  • 如果项目既是内容站,又需要局部动态能力(比如用户评论区、搜索页),我的经验是:框架选哪个都行,但架构上一定要把静态和动态的边界划分清楚。哪些路由是构建时生成,哪些路由走客户端渲染,这个决定了后期维护的舒适度。

最后再分享一个我个人的经验:做了这么多 SSG 项目之后,我发现最后的瓶颈通常不在框架,而在内容组织方式。静态站点的上限,取决于你把内容模型设计得够不够清晰。框架只是把内容变成页面的流水线,流水线再好,原料不行也白搭。所以动手写代码之前,先把内容结构、分类体系、数据接口这些最基础的事情梳理好,再挑框架不迟。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询