React Server Components 是什么?一文讲清 CSR、Server Components 与 Next.js 中的客户端/服务端组件

在学习 React 的过程中,我们通常会先接触到"组件""状态""事件""Hooks"这些概念。随着项目变大、对性能和 SEO 的要求越来越高,一个绕不开的话题就出现了:React 组件到底是在浏览器渲染,还是在服务器渲染?

这篇文章就围绕这个问题,系统梳理以下几个概念:

  1. 什么是客户端渲染(CSR)
  2. 传统 React 的问题在哪里
  3. 什么是 React Server Components
  4. 为什么不能直接在原生 React 中随便用 Server Components
  5. Next.js 中默认的 Server Component 和 use client 到底是什么意思

如果你刚开始接触 Next.js,或者最近听到了 React 19、Server Components、App Router 这些概念,但还没有完全理清,这篇文章可以帮你建立一个比较清楚的整体认识。

一、传统 React 是如何渲染页面的?

我们通常说 React 是一个用于构建用户界面的 JavaScript 库。更具体一点说,在传统使用方式下,React 主要运行在浏览器端

一个普通 React 应用的大致流程如下:

  1. 首先,开发者编写 React 代码。开发完成后,再使用 webpack、Vite 之类的构建工具,把项目打包成适合上线部署的前端资源。随后,这些资源会被部署到服务器上。

  2. 当用户访问这个网站时,服务器通常返回的是一个非常基础的 HTML 文件。这个 HTML 文件往往只有一个像下面这样的结构:

    <script src="/bundle.js"></script>

也就是说,最开始浏览器拿到的 HTML 几乎是空的,真正的页面内容并不在初始 HTML 里。然后浏览器继续下载 JavaScript bundle,执行 JavaScript 代码,React 才开始运行:

  1. 挂载组件
  2. 构建组件树
  3. 进行 reconciliation(协调)
  4. 最终把真正的 UI 渲染到 root 节点中

这个过程,就是我们常说的客户端渲染,也就是 CSR(Client Side Rendering)。

二、什么是客户端渲染(CSR)?

所谓客户端渲染,本质上就是:

页面内容不是服务器直接返回好的,而是浏览器拿到基础 HTML 后,再通过 JavaScript 动态生成页面内容。

从开发体验来看,这种方式非常灵活,也正是 React 流行起来的重要原因之一。

但客户端渲染也不是没有代价。随着项目复杂度增加,它的缺点会越来越明显。

三、传统 CSR 的两个主要问题

1. 初始 HTML 内容过少,不利于 SEO

CSR 最大的问题之一,就是首屏 HTML 往往过于空洞

对于用户来说,这会导致一个常见现象:页面在 JavaScript 还没加载执行完成之前,可能会先出现短暂的空白。

对于搜索引擎来说,这个问题更明显。搜索引擎爬虫访问页面时,最先看到的是服务器返回的初始 HTML。如果这个 HTML 几乎没有内容,只有一个 div#root 和几个脚本标签,那么爬虫就很难准确理解页面内容。

比如一个商品列表页面,用户最终明明能看到"产品标题""商品列表""价格"等信息,但在初始 HTML 里,这些内容可能根本不存在。搜索引擎看到的只是一个空壳页面,这显然不利于 SEO 和自然流量获取。

2. JavaScript Bundle 过大,影响性能

另一个问题是性能。

随着 React 应用越来越复杂,前端 bundle 往往会越来越大。用户访问页面时,不仅要下载这些 JS,还要解析、执行,然后 React 才能真正把页面渲染出来。

这在桌面设备上有时还不那么明显,但在移动端或者弱网环境下,就很容易出现这些问题:

  • 首屏显示慢
  • 页面可交互时间变晚
  • 用户感觉"页面加载出来很慢"
  • 低性能设备体验下降

所以,传统 CSR 虽然开发方便,但在 SEO 和性能方面都存在天然短板。

四、React Server Components 是什么?

为了解决这些问题,React 引入了一个很重要的能力:++Server Components++。

它的核心思想并不复杂:

不是所有 React 组件都必须在浏览器里执行。某些组件完全可以先在服务器上运行,并直接生成 HTML。

也就是说,组件可以在服务端完成以下事情:

  • 获取数据
  • 运行组件逻辑
  • 预渲染内容
  • 生成 HTML

然后服务器把这份已经准备好的内容发给浏览器。浏览器的职责主要就变成了:显示这些内容

这和传统 CSR 有一个根本区别:

  • CSR:先给浏览器空 HTML,再靠 JS 动态生成 UI
  • Server Components:服务器先把内容准备好,再把结果发给浏览器

五、Server Components 能解决什么问题?

1. 改善 SEO

因为页面内容已经在服务器上准备好了,所以搜索引擎爬虫访问页面时,拿到的是带内容的 HTML,而不是一个空壳。

这就使得页面更容易被正确抓取和索引。

2. 减少客户端 Bundle 体积

如果一个组件完全在服务器端执行,那么它的很多逻辑代码就不需要发送到浏览器。

这意味着:

  • 客户端 JavaScript 更少
  • 下载体积更小
  • 浏览器执行压力更低
  • 页面性能更好

所以,Server Components 的价值并不仅仅是"服务端渲染"这四个字,而是它能在架构层面减少前端负担。

六、React 已经有 Server Components 了,为什么还要 Next.js?

这里是很多初学者最容易混淆的地方。

虽然 Server Components 是 React 的能力,但React 本身并不是一个完整的服务端运行框架。传统意义上的 React 更像是一个专注于 UI 的库,它本身没有帮你搭好完整的服务端执行环境。

简单说就是:

React 提供了 Server Components 这种能力,但你还需要一个"能让这些组件在服务器运行起来"的框架。

而 Next.js 就是最典型的选择之一。

Next.js 构建在 React 之上,不只是一个前端路由工具,而是一个完整的 React 全栈框架。它能够:

  • 在服务器运行 React 组件
  • 处理服务端渲染
  • 支持 App Router
  • 默认支持 Server Components

所以,当你真正想在项目中使用 React Server Components 时,最常见的做法就是使用 Next.js。

七、在 Next.js 中,组件默认就是 Server Component

在 Next.js 的 App Router 模式下,一个很重要的默认规则是:

组件默认都是 Server Components。

也就是说,如果你在 app 目录里写一个普通组件,只要它没有使用客户端专属能力,那么它默认就会被当作服务端组件处理。

这意味着你可以在这些组件里:

  • 获取服务端数据
  • 渲染静态内容
  • 输出 HTML
  • 减少发送到客户端的 JS

这种默认行为,其实正是 Next.js 和传统 React 项目的一个重要区别。

八、什么时候需要 Client Component?

虽然 Server Components 很强,但不是所有组件都适合放在服务端。

如果一个组件需要这些能力:

  • useState
  • useEffect
  • useRef
  • 浏览器事件处理,比如 onClick
  • 浏览器 API,比如 window、document

那么它就不能继续是一个纯服务端组件了。因为这些东西本质上需要在浏览器里运行。

这时,你就需要把它声明成 Client Component

九、怎么把组件变成 Client Component?

方法非常简单:

在组件文件顶部加上一行:

复制代码
"use client";

这是一条特殊指令。它告诉 Next.js:

这个组件需要在客户端运行。

比如下面这个组件里使用了 useState:

复制代码
"use client";

import { useState } from "react";

export default function Counter() {
  const [count, setCount] = useState(0);

  return (
    <>
      <p>count: {count}</p>
      <button onClick={() => setCount(count + 1)}>+1</button>
    </>
  );
}

如果没有 "use client",这个组件在 Next.js 中就会报错,因为 useState 只能在 Client Component 中使用。

十、Server Component 和 Client Component 的区别

可以把它们这样理解:

Server Component

  • 默认运行在服务器
  • 适合数据获取和预渲染
  • 不适合交互逻辑
  • 不直接使用 useState、useEffect、事件处理等客户端能力
  • 不会把全部组件逻辑都发送到浏览器

Client Component

  • 运行在浏览器
  • 适合交互和状态管理
  • 可以使用 Hooks 和事件
  • 需要显式写 "use client"

所以在 Next.js 中,通常不是"全是服务端组件"或者"全是客户端组件",而是两者结合使用。

一个很典型的页面结构可能是:

  • 页面主体、列表展示、数据获取:Server Components

  • 按钮点击、表单状态、交互控件:Client Components

十一、为什么说 Next.js 是 React Server Components 的最佳实践场景?

因为 Next.js 帮你做了很多底层工作,让你不必从零搭建服务端环境。

比如你创建一个 Next.js 项目,使用 App Router 后:

  • 页面组件默认就是 Server Components
  • 可以直接写服务端组件逻辑
  • 需要交互时,再用 "use client" 切换为客户端组件

这种模式非常自然,也很符合现代 React 项目的组织方式。

从开发者角度看,你不再需要把整个页面都丢给浏览器,而是可以更细粒度地思考:

  • 哪部分应该在服务器完成?
  • 哪部分必须在浏览器里交互?
  • 哪部分代码没有必要进客户端 bundle?

这种"拆分渲染责任"的能力,就是 Server Components 带来的真正价值。

十二、一个简单的理解方式

如果你还是觉得这些概念有点绕,可以用一句话来记:

能在服务器提前准备好的内容,就尽量交给 Server Component。

必须依赖浏览器交互的部分,再交给 Client Component。

再压缩一点:

  • 页面上的"内容"更适合 Server Components
  • 页面上的"交互"更适合 Client Components

十四、什么是预渲染(Pre-rendering)?

预渲染,简单说就是:

在用户打开页面之前,先把页面的 HTML 内容准备好。

这里最关键的一点是:

用户一打开页面,浏览器拿到的 HTML 不是空壳,而是已经有页面结构和内容。

比如一个商品列表页,如果做了预渲染,那么服务器返回的 HTML 可能一开始就像这样:

复制代码
<h1>Product List</h1>
<ul>
  <li>iPhone</li>
  <li>MacBook</li>
  <li>AirPods</li>
</ul>

这样用户和搜索引擎一开始都能看到内容。

所以预渲染的本质不是"让页面能点击",而是:

先把页面内容准备出来。

它最主要解决的是:

  • 首屏显示
  • SEO
  • 页面初始可见性

十五、预渲染和客户端渲染的区别

客户端渲染(CSR)

在 CSR 下,服务器最开始返回的 HTML 常常很空,比如:

复制代码
<div id="root"></div>
<script src="/bundle.js"></script>

浏览器拿到后,要先:

  1. 下载 JS
  2. 执行 JS
  3. React 运行
  4. 创建组件树
  5. 渲染内容

也就是说:

内容是浏览器自己算出来的。

预渲染(Pre-rendering)

在预渲染下,服务器会先把页面 HTML 准备好,再发给浏览器。

所以浏览器一开始看到的就已经是内容了。

比如标题、列表、文本、图片结构等,都已经在 HTML 里。

也就是说:

内容是服务器先生成好的。

所以一句话区分:

  • CSR:先空壳,后靠 JS 出内容。
  • 预渲染:先有内容,再考虑交互。

十六、那什么是水合(Hydration)?

这个词最容易让人懵。

Hydration 直译叫"水合",但你不要按化学去想。

在 React 里,它的意思其实是:

浏览器接管服务器提前渲染好的 HTML,并给它绑定 React 的事件和状态逻辑。

你可以这样理解:

服务器已经帮你把页面"画出来了"。

但这时候页面还只是"静态长相"。

浏览器接下来下载 React 的 JS 代码,然后 React 会检查:

"这个 HTML 是不是和我组件渲染出来的一致?"

如果一致,React 就不会重新整个重画一遍,而是直接:

把事件绑定上去

把状态系统接上去

让组件变得可交互

这个过程,就叫 Hydration

所以:

Hydration 不是重新渲染页面内容本身,而是让已有 HTML 变成一个真正由 React 控制、可交互的页面。

十七、为什么需要 Hydration?

因为服务器虽然能先把 HTML 给你生成出来,

但服务器不能替浏览器完成所有前端交互。

比如这些事情,最终还是得在浏览器里发生:

  1. 按钮点击
  2. 输入框输入
  3. 状态变化
  4. 弹窗开关
  5. 本地交互
  6. 浏览器事件监听

服务器只能先把"长什么样"给你。

浏览器还得负责"怎么动起来"。

所以 Hydration 的作用就是:

把"已经显示出来的静态页面",升级成"真正能交互的 React 页面"。

十八、一个最简单的例子

假设你有一个计数器组件:

复制代码
function Counter() {
  const [count, setCount] = useState(0)

  return (
    <button onClick={() => setCount(count + 1)}>
      Count: {count}
    </button>
  )
}

如果服务器先预渲染,它可能先输出这样一个 HTML:

复制代码
<button>Count: 0</button>

用户打开页面时,已经能看见按钮了。

++但这时候如果 JS 还没加载完,这个按钮其实还不能真正响应 React 的状态逻辑。++

当浏览器把 React 代码下载并执行之后,React 会把这个按钮"接管"过来:

  • 绑定 onClick
  • 建立 count 的状态逻辑
  • 让按钮点一下后能变成 Count: 1

这个"接管"的过程就是 Hydration。

所以你要注意:

Hydration 发生时,HTML 已经在页面上了。

它不是"生成 HTML",而是"让 HTML 活起来"。

十九、预渲染和 Hydration 的关系

你可以把它们理解成一个两步流程。

第一步:预渲染

服务器先生成 HTML

浏览器拿到后立刻能显示内容

第二步:Hydration

浏览器下载 React 的 JS

React 接管这份 HTML

页面获得交互能力

所以:

预渲染解决"先看到"

Hydration 解决"再能点"

这两个动作经常一起出现,但它们不是同一个东西。

二十、为什么很多人说"页面已经出来了,但还不能点"?

这其实就是预渲染和 Hydration 之间的时间差。

一个页面可能已经把 HTML 发出来了,所以你肉眼能看见内容;

但如果 JS 还没下载完,或者 Hydration 还没完成,那么某些交互可能还没准备好。

这时候就会出现这种现象:

  • 页面已经显示了
  • 但是按钮点击没反应
  • 或者交互有短暂延迟

这说明:

HTML 已经预渲染完成,但 Hydration 还没彻底结束。

二十一、Server Components 和 Hydration 有什么关系?

这是你接下来在 Next.js 里特别容易混的点。

不是所有服务端出来的内容都需要完整 Hydration。

传统 SSR + 客户端 React

如果一个页面是服务端先渲染 HTML,再由 React 在客户端接管,那通常会有 Hydration 过程。

React Server Components

Server Components 更进一步,它的目标之一就是:

尽量减少需要发到客户端的 JavaScript。

也就是说:

  • 有些组件纯粹在服务器运行
  • 它们只负责产出内容
  • 它们本身不会在浏览器里变成一个完整的可交互 React 组件

这种情况下,Hydration 的重点就主要落在那些真正的 Client Components 上。

你可以这样理解:

  • Server Component:更像"纯内容输出"
  • Client Component:更像"需要在浏览器里活起来的部分"

所以在 Next.js 里:

  • 纯 Server Component 不强调浏览器交互
  • 带 "use client" 的组件才更直接参与客户端状态和 Hydration

二十二、最容易混淆的几个概念

1. 预渲染 = 服务端渲染吗?

不完全等于,但关系很近。

预渲染强调的是:

HTML 在用户打开前就已经准备好。

这个 HTML 可能来自:

  • 服务端实时渲染
  • 构建时静态生成
  • 某种服务器端预生成机制

所以预渲染是一个更大的概念。

2. Hydration = 再渲染一次吗?

不准确。

Hydration 不是"从零重新生成 HTML",

而是:

在已经存在的 HTML 上,补上 React 的运行能力。

所以更像"接管"和"激活",而不是单纯"重画"。

3. CSR 有 Hydration 吗?

通常我们谈严格意义上的 Hydration,更多是指:

页面已经有服务端或预生成 HTML,然后 React 在客户端接管。

纯 CSR 一开始几乎没内容,所以严格来说,它更多是"客户端首次渲染",不是"对现有 HTML 的水合"。

二十三、总结

React、Next.js、Server Components、预渲染和 Hydration 这些概念,初看起来很容易混在一起,但如果抓住"内容什么时候出来 "和"页面什么时候能交互"这两个核心问题,其实就会清楚很多。

传统 React 大多数场景下采用的是客户端渲染(CSR)。在这种模式下,服务器最开始返回的 HTML 往往只是一个空壳,真正的页面内容要等浏览器下载并执行 JavaScript 后,React 才会把 UI 渲染出来。这种方式开发灵活,但也带来了两个明显问题:一是初始 HTML 内容太少,不利于 SEO;二是随着项目变大,客户端 JavaScript bundle 也会越来越重,从而影响首屏性能和用户交互体验。

为了解决这些问题,React 引入了 Server Components。它的核心价值在于:让一部分组件可以直接在服务器执行,提前生成内容,再把结果发给浏览器。这样做的好处非常直接:搜索引擎更容易抓取页面内容,客户端也不必承担所有组件逻辑的执行成本,从而减轻浏览器负担。

不过,React 虽然提供了 Server Components 的能力,但它本身并不是一个完整的服务端运行框架。因此,在实际项目中,我们通常借助 Next.js 来真正落地这一能力。在 Next.js 的 App Router 中,组件默认就是 Server Component ;只有当组件需要使用 useState、useEffect、事件处理、浏览器 API 等客户端能力时,才需要通过 "use client" 将其显式声明为 Client Component

与此同时,理解 预渲染(Pre-rendering)Hydration(水合) 也很重要。预渲染解决的是"页面内容能不能先显示出来",也就是服务器能否提前把 HTML 内容准备好;Hydration 解决的是"这些已经显示出来的内容,什么时候真正拥有 React 的交互能力"。换句话说,预渲染负责先让页面"看得见",Hydration 负责再让页面"动起来"

所以,整篇文章最核心的结论其实可以归纳成一句话:

现代 React/Next.js 的关键,不只是学会写组件,而是学会合理拆分:哪些内容应该在服务器提前完成,哪些交互必须留给浏览器处理。

当你真正理解了这点,你就不会再把 Server Component、Client Component、预渲染和 Hydration 看成几个彼此割裂的概念,而会把它们看成同一套渲染体系中的不同环节:

  • CSR:内容主要靠浏览器执行 JS 后生成
  • 预渲染:服务器提前把页面内容准备好
  • Hydration:浏览器接管 HTML,让页面具备交互能力
  • Server Component:适合做服务端内容输出
  • Client Component:适合做浏览器交互和状态管理

理解这些概念之后,再去学习 Next.js 的 App Router、数据获取方式、页面性能优化和组件拆分策略,就会顺畅很多。

相关推荐
肉肉不吃 肉2 小时前
事件循环,宏任务,微任务
前端·javascript
z止于至善2 小时前
Vue ECharts:Vue 生态下的 ECharts 可视化最佳实践
前端·vue.js·echarts·vue echarts
℘团子এ2 小时前
什么是Docker
前端·docker·容器
Software攻城狮2 小时前
【el-table 表格组件 删除标头分割线】
前端·vue.js·elementui
陆康永2 小时前
vue2封装hook函数,可以监听主页面生命周期
前端·javascript·vue.js
我命由我123452 小时前
Vue Router - 记录一下 2 种路由写法
前端·javascript·vue.js·前端框架·html·html5·js
m0_719084112 小时前
导入导出—设备管理系统
前端·javascript·vue.js
周淳APP2 小时前
【计算机网络之XSS、CSRF、DDoS原理及防御措施】
前端·网络·计算机网络·http·ddos·xss·csrf
wuhen_n2 小时前
Vue Router 进阶:路由懒加载、导航守卫与元信息的高效运用
前端·javascript·vue.js