Server Components
本教程共 42 篇 · 第 6 篇 · 更新于 2026-07-30 · 约 7 分钟阅读
6. Server Components
本节目标:理解 Server Components 是什么、为什么需要它、它的限制,以及何时使用它。
为什么需要 Server Components
传统 React 应用是客户端渲染——浏览器下载 JS 包,执行 React 代码,生成 DOM。这带来三个问题:
- 首屏慢:得等 JS 下载执行完,用户才看到内容
- 包体积大:所有组件代码(包括只在服务端用的)都发给浏览器
- 数据获取复杂:用
useEffect取数据,有瀑布请求问题
Server Components 让组件在服务器上执行,只把渲染结果(HTML + RSC Payload)发给客户端。
什么是 Server Components
Server Components 是 React 18+ 的新特性。在 Next.js App Router 中,所有组件默认都是 Server Components。
// 这就是一个 Server Component
// app/posts/page.tsx
export default async function Page() {
const posts = await db.post.findMany()
return (
<ul>
{posts.map(post => (
<li key={post.id}>{post.title}</li>
))}
</ul>
)
}
这个组件:
- 在服务器上执行
- 可以直接访问数据库
- 可以用
async/await获取数据 - 不发送 JS 到客户端
Server Components 的优势
1. 直接访问后端资源
// 可以直接查数据库
const users = await db.query('SELECT * FROM users')
// 可以直接读文件
const content = fs.readFileSync('./data.json', 'utf-8')
这些代码永远不会出现在客户端包里,密钥、密码不会泄露。
2. 更小的客户端包
Server Component 的代码不需要下载到浏览器。这意味着:
- 页面加载更快
- 首屏渲染更快(直接显示 HTML)
- 低端设备更友好
3. 更好的数据获取
Server Components 支持 async/await,没有 useEffect 的瀑布问题:
// 并行请求(没有 waterfall)
export default async function Page() {
const users = getUsers()
const posts = getPosts()
const [usersData, postsData] = await Promise.all([users, posts])
return <div>...</div>
}
4. 更好的 SEO
HTML 直接在服务器生成,搜索引擎能完整抓取内容。
Server Components 的限制
Server Components 不是万能的,有几个硬性限制:
不能用 useState / useEffect
Server Components 在服务器上运行一次就结束了,没有组件生命周期:
// ❌ 错误:Server Component 不能用 useState
export default function Page() {
const [count, setCount] = useState(0) // 报错!
return <div>{count}</div>
}
不能用浏览器 API
// ❌ 错误:没有 window、localStorage
export default function Page() {
const width = window.innerWidth // 报错!
const data = localStorage.getItem('key') // 报错!
}
不能用事件处理
// ❌ 错误:没有 onClick、onChange
export default function Page() {
return <button onClick={() => alert('hi')}>点击</button>
}
不能用自定义 Hook(依赖状态的)
自定义 Hook 如果用了 useState、useEffect,Server Component 也不能用。
什么时候用 Server Components
| 场景 | 用 Server Component |
|---|---|
| 获取数据 | 是 |
| 直接访问数据库 | 是 |
| 使用 API 密钥 | 是 |
| 渲染静态内容 | 是 |
| 需要 SEO | 是 |
| 需要交互(点击、输入) | 否 |
| 需要状态(useState) | 否 |
| 需要生命周期(useEffect) | 否 |
| 需要浏览器 API | 否 |
Server 和 Client 如何协作
一个页面可以混用两种组件。典型的模式:
// Server Component:获取数据
import LikeButton from '@/components/like-button'
import { getPost } from '@/lib/data'
export default async function Page({
params,
}: {
params: Promise<{ id: string }>
}) {
const { id } = await params
const post = await getPost(id)
return (
<article>
<h1>{post.title}</h1>
<p>{post.content}</p>
{/* Client Component 处理交互 */}
<LikeButton likes={post.likes} />
</article>
)
}
// Client Component:处理交互
'use client'
import { useState } from 'react'
export default function LikeButton({ likes }: { likes: number }) {
const [count, setCount] = useState(likes)
return (
<button onClick={() => setCount(count + 1)}>
赞 {count}
</button>
)
}
渲染流程
服务端
- Server Components 渲染为 RSC Payload(一种紧凑的二进制格式)
- Client Components 的位置用占位符标记
- 两者结合生成预渲染的 HTML
客户端(首次加载)
- HTML 立即显示非交互预览
- RSC Payload 用于协调组件树
- JS 下载后”注水”(hydrate)Client Components,让它们可交互
后续导航
- RSC Payload 被预取并缓存
- Client Components 完全在客户端渲染,不需要服务器 HTML
数据传输
Server Component 向 Client Component 传数据,通过 props:
// Server Component
const user = await getUser()
<ClientComponent user={user} />
Important传给 Client Component 的 props 必须是可序列化的。函数、类实例不能直接传。
如果需要传函数,把函数定义为 Server Action(用 "use server" 标记)。
防止环境污染
有些代码只能在服务端运行(比如用 API 密钥的函数)。如果不小心在 Client Component 里导入,密钥会泄露。
Next.js 提供 server-only 包防止这种情况:
npm install server-only
// lib/data.ts
import 'server-only'
export async function getData() {
const res = await fetch('https://api.example.com', {
headers: { Authorization: process.env.API_KEY }
})
return res.json()
}
如果 Client Component 导入这个文件,构建时会报错。
小结
这一章我们理解了 Server Components:
- 默认是 Server Component,除非加
"use client" - 优势:直连后端、小包、并行请求、SEO 友好
- 限制:不能用 state、effect、浏览器 API、事件处理
- 典型模式:Server 获取数据,Client 处理交互
- 用
server-only防止服务端代码泄露到客户端
下一章,我们来详细学习 Client Components。