服务端组件 (Server Components)

React 服务端组件 (Server Components) 允许您编写可在服务端渲染并选择性缓存的用户界面。在 Next.js 中,渲染工作会按路由段进一步拆分以实现流式传输和部分渲染,并提供三种不同的服务端渲染策略:

本文将介绍服务端组件的工作原理、适用场景以及不同的服务端渲染策略。

服务端渲染的优势

在服务端执行渲染工作具有以下优势:

  • 数据获取:服务端组件允许将数据获取移至更靠近数据源的服务端,通过减少渲染所需数据的获取时间及客户端请求次数来提升性能。
  • 安全性:服务端组件可确保敏感数据和逻辑(如令牌和 API 密钥)保留在服务端,避免泄露给客户端。
  • 缓存:服务端渲染结果可被缓存并在后续请求及不同用户间复用,通过减少每次请求的渲染和数据获取量来提升性能并降低成本。
  • 包体积:服务端组件允许将原本会影响客户端 JavaScript 包体积的大型依赖保留在服务端,这对网络较慢或设备性能较低的用户尤为有益,因为客户端无需为服务端组件下载、解析和执行任何 JavaScript。
  • 初始页面加载与 首次内容绘制 (FCP):服务端可直接生成 HTML,让用户无需等待客户端下载、解析和执行渲染页面所需的 JavaScript 即可立即查看页面。
  • 搜索引擎优化与社交网络分享:生成的 HTML 可被搜索引擎爬虫用于索引页面,或被社交网络爬虫用于生成页面预览卡片。
  • 流式传输:服务端组件允许将渲染工作拆分为多个块,并在就绪后流式传输至客户端,使用户无需等待整个页面在服务端完成渲染即可提前看到部分内容。

在 Next.js 中使用服务端组件

Next.js 默认使用服务端组件,无需额外配置即可自动实现服务端渲染。您也可以根据需要选择使用客户端组件 (Client Components),详见客户端组件

服务端组件如何渲染?

在服务端,Next.js 使用 React 的 API 来协调渲染工作。渲染任务会被拆分为多个块:按独立路由段和 Suspense 边界划分。

每个块的渲染分为两个步骤:

  1. React 将服务端组件渲染为一种特殊数据格式 —— React 服务端组件负载 (RSC Payload)
  2. Next.js 使用 RSC 负载和客户端组件的 JavaScript 指令在服务端生成 HTML

随后在客户端:

  1. HTML 被用于立即展示路由的快速非交互式预览 —— 这仅适用于初始页面加载。
  2. React 服务端组件负载用于协调客户端与服务端组件树,并更新 DOM。
  3. JavaScript 指令用于水合 (hydrate) 客户端组件,使应用具备交互性。

什么是 React 服务端组件负载 (RSC)?

RSC 负载是渲染后的 React 服务端组件树的紧凑二进制表示形式,供 React 在客户端更新浏览器 DOM 时使用。RSC 负载包含:

  • 服务端组件的渲染结果
  • 客户端组件应渲染位置的占位符及其 JavaScript 文件引用
  • 从服务端组件传递给客户端组件的任何 props

服务端渲染策略

服务端渲染分为三种子类型:静态渲染、动态渲染和流式渲染。

静态渲染(默认)

通过静态渲染,路由会在构建时或在数据重新验证后的后台进行渲染。结果会被缓存并可推送至内容分发网络 (CDN),使得渲染结果能在用户和服务器请求间共享。

静态渲染适用于路由数据无需用户个性化且可在构建时确定的场景,如静态博客文章或产品页面。

动态渲染

通过动态渲染,路由会在请求时为每个用户进行渲染。

动态渲染适用于路由数据需用户个性化或只能在请求时获取的场景,如 cookies 或 URL 的搜索参数。

使用缓存数据的动态路由

大多数网站的路由并非完全静态或完全动态 —— 而是一个连续区间。例如,电商页面可能使用定期重新验证的缓存产品数据,同时也包含未缓存的个性化用户数据。

在 Next.js 中,您可以拥有同时包含缓存和未缓存数据的动态渲染路由。这是因为 RSC 负载与数据是分开缓存的,使您可以选择动态渲染而无需担心在请求时获取所有数据对性能的影响。

了解更多关于全路由缓存数据缓存的信息。

切换至动态渲染

在渲染过程中,如果检测到动态函数未缓存数据请求,Next.js 会将整个路由切换为动态渲染。下表总结了动态函数和数据缓存如何影响路由的渲染方式:

动态函数数据路由
已缓存静态渲染
已缓存动态渲染
未缓存动态渲染
未缓存动态渲染

上表中,要使路由完全静态,所有数据必须已缓存。但您也可以创建同时使用缓存和未缓存数据获取的动态渲染路由。

作为开发者,您无需手动选择静态或动态渲染,Next.js 会根据使用的功能和 API 自动为每个路由选择最佳渲染策略。您只需决定何时缓存或重新验证特定数据,并可以选择流式传输部分 UI。

动态函数

动态函数依赖于只能在请求时获取的信息,如用户的 cookies、当前请求头或 URL 的搜索参数。在 Next.js 中,这些动态函数包括:

  • cookies()headers():在服务端组件中使用这些函数会使整个路由在请求时转为动态渲染。
  • useSearchParams()
    • 在客户端组件中使用会跳过静态渲染,改为在客户端渲染直至最近的父级 Suspense 边界。
    • 建议将使用 useSearchParams() 的客户端组件包裹在 <Suspense/> 边界中,这样其上层的客户端组件仍可静态渲染。示例
  • searchParams:在页面属性中使用会使页面在请求时转为动态渲染。

使用以上任何函数都会使整个路由在请求时转为动态渲染。

流式渲染

流式渲染期间路由段并行化示意图,展示单个块的数据获取、渲染和水合过程。

流式渲染允许您逐步从服务端渲染 UI。工作被拆分为多个块,并在就绪后流式传输至客户端。这使得用户无需等待全部内容完成渲染即可立即看到页面的部分内容。

客户端部分渲染页面示意图,显示正在流式传输的块的加载状态。

流式渲染已内置在 Next.js 应用路由中,这既提升了初始页面加载性能,也优化了依赖较慢数据获取的 UI(否则会阻塞整个路由渲染)。例如产品页面的评论部分。

您可以通过 loading.jsReact Suspense 组件来启动路由段的流式传输。更多信息请参阅加载状态与流式传输章节。

On this page