Next.js 服务端渲染:SSR、SSG 与 API 路由

FreeGuideOnline 最新 2026-06-15

什么是 Next.js 与渲染策略

Next.js 是一个基于 React 的全栈框架,它提供了开箱即用的服务端渲染(SSR)静态站点生成(SSG) 以及 增量静态再生(ISR) 等能力。开发者无需手动配置 Webpack、路由及服务器逻辑,就能构建高性能的 Web 应用。

在传统 React 单页应用(SPA)中,浏览器下载一个几乎为空的 HTML 文件,再由 JavaScript 在客户端完成页面渲染。这会导致首屏白屏时间长、SEO 不友好。Next.js 则允许你在服务端完成第一次页面渲染,将完整的 HTML 直接发送给浏览器,从而解决上述问题。

Next.js 提供了三种核心的页面渲染方式:

  • SSR(Server-Side Rendering):每次请求时在服务端生成页面。
  • SSG(Static Site Generation):在构建时预先生成静态页面。
  • ISR(Incremental Static Regeneration):在 SSG 的基础上,按需重新生成静态页面。

理解它们的差异与适用场景,是利用 Next.js 构建应用的关键。

服务端渲染(SSR)

SSR 的工作原理

当你需要实时数据(例如用户登录状态、个性化内容、数据库最新查询结果)时,SSR 是最直接的选择。每次用户通过浏览器请求页面时,Next.js 都会在服务器上执行对应页面的 getServerSideProps 函数,获取数据并渲染 React 组件为 HTML 字符串,然后将完整的 HTML 响应给客户端。

SSR 的生命周期如下:

  1. 浏览器发起页面请求。
  2. Next.js 服务器调用该页面的 getServerSideProps 函数。
  3. 函数从外部 API、数据库或文件系统中获取所需数据。
  4. React 组件使用这些数据被渲染成 HTML。
  5. 服务器将包含完整内容的 HTML 返回给浏览器。
  6. 客户端浏览器接管页面(水合,hydration),使其变为可交互的 React 应用。

实现 SSR

在 Next.js 页面文件(pages 目录下,或 app 目录下使用 force-dynamic)中,你需要导出一个名为 getServerSideProps 的异步函数:

// pages/ssr-page.js
export default function SSRPage({ data }) {
  return (
    <div>
      <h1>服务端渲染页面</h1>
      <p>从服务器获取的数据{data}</p>
    </div>
  );
}

export async function getServerSideProps(context) {
  // context 中包含请求参数、req、res 等信息
  const res = await fetch('https://api.example.com/data');
  const data = await res.json();

  return {
    props: {
      data: data.message, // 将数据作为 props 传递给页面组件
    },
  };
}

关键点

  • getServerSideProps每次请求时都在服务端运行,因此它总能获取到最新的数据。
  • 函数的返回值必须是一个对象,其中 props 属性会被传递给页面组件。
  • 你可以在函数内访问请求上下文,例如 headers、cookies 等,实现权限校验。

SSR 的优缺点

优点

  • 首屏渲染完整,对 SEO 极其友好。
  • 内容永远是最新的,适合高度动态的页面。
  • 可以针对不同用户返回不同内容,实现个性化。

缺点

  • 每个请求都需要等待数据获取和页面渲染,TTFB(Time to First Byte)可能较高。
  • 服务器负载随访问量线性增长,冷启动或高并发时性能压力较大。
  • 不适合内容很少变化却承受大量访问的公共页面。

静态站点生成(SSG)

SSG 的概念

对于内容不经常变化的页面(如博客文章、文档、产品介绍页),在每次请求时重新渲染是一种资源浪费。静态站点生成(SSG) 的做法是:在构建时next build)预先获取数据,并生成对应的静态 HTML 文件。后续当用户请求这些页面时,服务器可以直接提供这些静态文件,无需实时计算。

Next.js 在构建时会调用页面中导出的 getStaticProps 函数,获取数据并生成静态页面。

实现 SSG

// pages/blog/[slug].js
export default function BlogPost({ post }) {
  return (
    <article>
      <h1>{post.title}</h1>
      <p>{post.content}</p>
    </article>
  );
}

export async function getStaticProps({ params }) {
  // 在构建时获取数据,params 来自 getStaticPaths
  const res = await fetch(`https://api.example.com/posts/${params.slug}`);
  const post = await res.json();

  return {
    props: {
      post,
    },
    // 可选:设置 revalidate 可实现 ISR(见后文)
  };
}

export async function getStaticPaths() {
  // 指定需要预渲染的动态路径
  const res = await fetch('https://api.example.com/posts');
  const posts = await res.json();

  const paths = posts.map((post) => ({
    params: { slug: post.slug },
  }));

  return {
    paths,
    fallback: false, // 或 true / 'blocking'
  };
}

关键点

  • getStaticProps 也返回包含 props 的对象,但它只在构建时调用一次(或配合 revalidate 进行增量更新)。
  • 对于包含动态路由的页面(如 [slug].js),必须同时导出 getStaticPaths 来告知 Next.js 哪些路径需要预渲染。
  • fallback 选项控制如何处理未预渲染的路径:false 表示返回 404;true 表示客户端动态请求并缓存;'blocking' 表示服务端等待生成完成后再返回页面。

SSG 的优点与限制

优点

  • 页面直接由 CDN 或静态文件服务器提供,加载速度极快,TTFB 极低。
  • 几乎无需服务器资源,可承受巨大流量。
  • SEO 最优,因为内容早在构建时就已经写入 HTML。

限制

  • 构建时间会随着页面数量增长而增加。
  • 如果数据在构建后发生变化,用户看到的将是旧内容,除非配合 ISR 或手动重新部署。
  • 不适合需要实时用户数据或高度动态的页面。

增量静态再生(ISR)

为什么需要 ISR

SSG 的痛点在于内容更新不及时。ISR 允许你在不重新构建整个站点的前提下,按需更新静态页面。你只需要在 getStaticProps 的返回值中加入一个 revalidate 字段(单位:秒)。

export async function getStaticProps() {
  const res = await fetch('https://api.example.com/trending');
  const data = await res.json();

  return {
    props: { data },
    revalidate: 60, // 最多 60 秒后,下一次请求将触发后台重新生成
  };
}

当页面发布后,第一个访问该页面的用户仍然会立即获得旧版本(stale),但 Next.js 会在后台触发重新生成。生成完成后,后续的用户将得到新页面,同时旧版本也会被替换。

ISR 结合了 SSG 的速度优势和数据的“接近实时性”,非常适合博客更新、电商商品页面等。

ISR 的工作流程

  1. 用户请求一个静态页面。
  2. Next.js 检查现有页面的生成时间是否已超过 revalidate 秒。
  3. 如果未超时,直接返回缓存页面(stale)。
  4. 如果超时,仍然立即返回旧页面,同时在后台触发重新生成(新数据拉取、新页面构建)。
  5. 新页面生成后,替换缓存,后续请求返回新页面。

需要注意:revalidate 的时间是“最多等待时间”,并不是严格的定时刷新。只有当新的请求到来且缓存过期时,才会触发再生。

三种渲染策略的选择指南

策略 数据更新频率 SEO 需求 示例场景
SSR(服务端渲染) 每次请求都实时 用户仪表盘、购物车、个性化内容
SSG(静态生成) 构建时固定,很少更新 最高 文档、博客(内容极少变动)、落地页
ISR(增加增量再生) 有一定更新频率,但可容忍短暂延迟 博客文章、商品详情页、新闻列表

有时你需要混合使用:首页用 SSG 或 ISR 保证速度,登录后的用户页面用 SSR 保证个性化。

API 路由:构建后端接口

Next.js 不仅负责前端页面的渲染,还能充当轻量级 API 服务器。你可以在 pages/api 目录下(或 app/api 目录下)创建 API 端点,这些路由只在服务端运行,不会打包到客户端代码中。

基本 API 路由

// pages/api/hello.js
export default function handler(req, res) {
  if (req.method === 'GET') {
    res.status(200).json({ message: 'Hello from Next.js API!' });
  } else {
    res.status(405).end(); // 方法不允许
  }
}

API 路由的 handler 函数接收标准的 req(http.IncomingMessage)和 res(http.ServerResponse)对象,你可以对其进行解析并返回 JSON 数据、处理表单提交等。

与 SSR/SSG 配合使用

API 路由常被用于:

  • 作为 BFF(Backend For Frontend):整合多个上游服务,为前端提供定制化数据接口。
  • 隐藏第三方 API 密钥:敏感请求从服务端 API 路由发出,避免在前端暴露。
  • 处理表单数据:接收前端表单提交并存入数据库或发送邮件。

getServerSidePropsgetStaticProps 中,也可以直接调用本地 API 路由,但更推荐直接操作数据库或调用外部服务,因为 API 路由在服务端调用时相当于发起 HTTP 请求,会引入不必要的网络开销。

动态 API 路由

和页面类似,API 路由也支持动态参数:

// pages/api/posts/[id].js
export default function handler(req, res) {
  const { id } = req.query;
  // 根据 id 从数据库查询
  res.status(200).json({ postId: id, title: '动态标题' });
}

API 路由的最佳实践

  • 做好方法判断,仅响应支持的 HTTP 方法。
  • 对输入进行验证和清洗,防止安全漏洞。
  • 为大型应用考虑将路由逻辑拆分为独立的控制器和服务层。
  • 利用 Next.js 的中间件(Middleware)实现权限校验、重定向等。

在 App Router(Next.js 13+)中的变化

自 Next.js 13 引入 App Router 后,原有的 pages 目录逐渐被 app 目录取代,渲染策略的实现方式也发生了变化:

  • getServerSideProps / getStaticProps 等函数不再直接使用。
  • 转为基于 React Server Components 的模型。
  • 在组件中直接通过 async 函数获取数据,并利用 fetchcache 选项控制静态/动态行为,或使用 unstable_noStorerevalidate 等指令。

示例(app 目录下的页面):

// app/blog/[slug]/page.js
export default async function Page({ params }) {
  const res = await fetch(`https://api.example.com/posts/${params.slug}`, {
    next: { revalidate: 60 }, // 类似 ISR
  });
  const post = await res.json();

  // 如果没有使用 next 选项,Next.js 默认会缓存该请求(类似 SSG)
  // 使用 cache: 'no-store' 可将其变成类似 SSR

  return <article>{post.content}</article>;
}

这种方式使得代码更加简洁,组件本身就是服务端组件。不过本教程的核心概念(SSR、SSG、ISR)在 App Router 下依然适用,只是实现细节有所更新。对于新项目,推荐使用 App Router;现有 Pages Router 仍可获得长期支持。

常见问题与调试技巧

  1. 数据未更新?
    检查是否使用了 getStaticProps 但遗漏 revalidate,或者浏览器缓存过强。尝试在开发模式下禁用缓存或使用无痕窗口测试。

  2. getServerSideProps 中请求超时?
    确保外部 API 可达,并在函数中添加错误处理(try/catch),避免整页崩溃。

  3. 构建时静态页面过多导致内存溢出?
    对于动态路由,使用 fallback: true'blocking' 延迟生成不常用的页面,而不是在构建时生成全部页面。

  4. API 路由无法访问?
    检查文件名是否在 pages/api 目录下,且导出的是一个函数。在 App Router 中需在 app/api 下使用 route.ts/js 文件。

总结

Next.js 通过 SSR、SSG、ISR 和 API 路由为开发者提供了构建现代 Web 应用的完整工具箱。选择合适的渲染策略可以显著提升用户体验和性能:

  • SSR 适合实时个性化内容。
  • SSG 提供最快的页面加载和最好的扩展性,适合变化极少的公开内容。
  • ISR 在两者之间取得平衡,适合需要一定时效性但又希望享受静态速度的页面。
  • API 路由 让你无需额外搭建后端即可实现接口逻辑。

掌握这些核心概念后,你就可以在实际项目中根据业务需求灵活搭配,构建出高性能、用户友好的应用。