全书目录

第五篇:Next.js 完全指南 —— 商业综合体宇宙

第四章:Server Components 和 Client Components —— 后厨与前台要分清

2 分钟 542 字 第 115 / 962 个阅读单元

这是 Next.js 最核心、也最容易混乱的地方。

在 App Router 里:

  • 默认是 Server Component
  • 只有文件顶部写了 'use client',它才进入客户端世界
#1. Server Component —— 后厨中央厨房

后厨负责:

  • 读数据库
  • 调内部服务
  • 拼首屏 HTML
  • 把结果交给前台展示
tsx
// 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
  • 不能直接访问 windowdocument
#2. Client Component —— 前台交互终端

前台负责:

  • 点击
  • 输入
  • 浏览器事件
  • 本地状态
  • 动画和即时交互
tsx
// 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. 最常见组合 —— 后厨取数,前台接管局部交互
tsx
// 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>
  );
}

一条铁律

text
Server Component
├── 可以渲染 Client Component
├── 可以直接取服务端数据
└── 负责首屏和数据边界

Client Component
├── 负责浏览器交互
├── 可以用 state/effect
└── 不要默认把整页都改成 client

很多新手一看到报错,就把整页文件头加上 'use client'
这相当于把整座综合体的后厨、仓库、财务室全搬到前台营业区,后面一定会乱。

#4. 'use client' 不是“给某个函数加功能”,而是划一条模块边界

React 官方文档特别强调:'use client' 会把这个模块,以及它继续导入的客户端依赖,一起拉进客户端执行边界。
所以它不是一个“随手加的标记”,而是架构边界。