这是 Next.js 最核心、也最容易混乱的地方。
在 App Router 里:
- 默认是 Server Component
- 只有文件顶部写了
'use client',它才进入客户端世界
#1. Server Component —— 后厨中央厨房
后厨负责:
- 读数据库
- 调内部服务
- 拼首屏 HTML
- 把结果交给前台展示
// app/products/page.tsx
async function getProducts() {
const res = await fetch('https://example.com/api/products');
return res.json();
}
export default async function ProductsPage() {
const products = await getProducts();
return (
<ul>
{products.map((item: { id: string; name: string }) => (
<li key={item.id}>{item.name}</li>
))}
</ul>
);
}Server Component 的特点:
- 可以直接
async - 可以在服务端取数
- 不能用
useState - 不能用
useEffect - 不能直接访问
window、document
#2. Client Component —— 前台交互终端
前台负责:
- 点击
- 输入
- 浏览器事件
- 本地状态
- 动画和即时交互
// app/products/LikeButton.tsx
'use client';
import { useState } from 'react';
export default function LikeButton() {
const [liked, setLiked] = useState(false);
return (
<button onClick={() => setLiked(v => !v)}>
{liked ? '已收藏' : '收藏'}
</button>
);
}#3. 最常见组合 —— 后厨取数,前台接管局部交互
// app/products/page.tsx
import LikeButton from './LikeButton';
async function getFeaturedProduct() {
return { id: 'p1', name: 'Next.js 指南' };
}
export default async function ProductsPage() {
const product = await getFeaturedProduct();
return (
<section>
<h1>{product.name}</h1>
<LikeButton />
</section>
);
}一条铁律:
Server Component
├── 可以渲染 Client Component
├── 可以直接取服务端数据
└── 负责首屏和数据边界
Client Component
├── 负责浏览器交互
├── 可以用 state/effect
└── 不要默认把整页都改成 client很多新手一看到报错,就把整页文件头加上 'use client'。
这相当于把整座综合体的后厨、仓库、财务室全搬到前台营业区,后面一定会乱。
#4. 'use client' 不是“给某个函数加功能”,而是划一条模块边界
React 官方文档特别强调:'use client' 会把这个模块,以及它继续导入的客户端依赖,一起拉进客户端执行边界。
所以它不是一个“随手加的标记”,而是架构边界。