Next.js 中的 React 服务器与客户端组件:理解差异

发布: (2025年12月31日 GMT+8 07:00)
6 min read
原文: Dev.to

Source: Dev.to

请提供您希望翻译的具体文本内容,我将为您翻译成简体中文。

介绍

React 团队在 React 18 中引入了 Server Components 作为实验性特性。它们允许 React 应用的部分在服务器上渲染,从而减少发送到客户端的 JavaScript 并实现更高效的数据获取。

Next.js 在其 App Router(在版本 13 中引入)中采用了这一概念。该框架将 React 的服务器渲染能力与自身的路由、数据获取和性能优化相结合。

Server Components

  • 仅在服务器上运行——它们永远不会在浏览器中执行。
  • 不能使用仅限客户端的特性,例如 useStateuseEffectwindow
  • 可以安全地从数据库或 API 获取数据,并仅返回生成的 HTML。
  • 从客户端包中排除 JavaScript,从而减小包体积并提升性能。
  • 可以导入 Client Components,但 Client Components 不能导入 Server Components。
// This is a Server Component (default)
export default async function ServerComponent() {
  const posts = await fetchPosts();
  return (
    <ul>
      {posts.map(post => (
        <li key={post.id}>- {post.title}</li>
      ))}
    </ul>
  );
}

默认情况下,Next.js App Routerapp/ 目录)中的所有组件都是 Server Components,除非另有标记。

客户端组件

客户端组件是传统的 React 组件,在浏览器中运行。当你需要交互、状态或副作用时必须使用它们。

要将文件标记为客户端组件,请在文件顶部添加 "use client" 指令:

"use client";

import { useState } from "react";

export default function Counter() {
  const [count, setCount] = useState(0);
  return (
    <button onClick={() => setCount(count + 1)}>
      Clicked {count} times
    </button>
  );
}

关键要点

  • 在服务器上渲染初始 HTML(预渲染),随后在浏览器中 hydrate(水化)。
  • 可以使用 React 状态 Hook(useStateuseEffect 等)以及浏览器 API。
  • 不能直接导入服务器组件,但可以通过 props 接收它们。

组合服务器组件和客户端组件

将服务器组件传递给客户端组件

服务器组件可以渲染另一个服务器组件,并将其作为属性传递给客户端组件。服务器渲染该节点、序列化它,客户端随后将其作为 React 元素接收。

// Server Component
export function ServerInfo() {
  return <div>Server time: {new Date().toISOString()}</div>;
}

// Client Component (Wrapper)
"use client";
export function Wrapper({ info }) {
  return (
    <section>
      ## Wrapped content:
      {info}
    </section>
  );
}

// Server Page
import { ServerInfo } from "./ServerInfo";
import { Wrapper } from "./Wrapper";

export default function Page() {
  return <Wrapper info={<ServerInfo />} />;
}

在服务器组件内部使用客户端组件

// Server Component (page)
import Counter from "./Counter"; // ✅ allowed (Client Component)

export default async function Page() {
  const user = await fetchUser();
  return (
    <section>
      <h1>Hello, {user.name}</h1>
      <Counter />
    </section>
  );
}

Hydration

Hydration 是 React 将交互性附加到服务器生成的静态 HTML 的过程。常见的陷阱包括:

  • 非确定性渲染(例如 Math.random()Date.now()),会导致服务器端和客户端生成不同的标记。
  • 服务器端和客户端之间的条件渲染差异。
  • 当服务器和客户端数据不一致时,属性(props)不匹配。

Best practices

  • 在同时在服务器端和客户端渲染的组件中使用确定性数据。
  • 将交互性隔离在小且定义明确的客户端组件中。
  • 将动态逻辑放入 useEffect 或其他仅客户端作用域中。

Comparison

功能服务器组件客户端组件
执行仅服务器服务器预渲染 + 浏览器水合
是否可以使用 React 状态钩子 (useState, useEffect)
安全的数据获取✅(直接)⚠️(通过 API 路由)
导入方向✅ 可导入客户端组件❌ 不能导入服务器组件
是否可以接收服务器组件作为属性
对包大小的影响最小增加包大小
SEO优秀也很好(已预渲染)
典型用例静态内容,数据密集型渲染交互式 UI、表单、动画

摘要

  • Server Components 适用于数据密集或静态内容;它们仅在服务器上运行,保持客户端包体积小,并提升 SEO。
  • Client Components 提供交互性;它们在服务器上预渲染,然后在浏览器中进行水化。
  • 导入流程遵循 Server → Client(允许),但 Client → Server 被禁止。
  • 将 Server Components 作为属性传递给 Client Components 可以实现强大的组合,同时保留两种渲染模型的优势。

如果正确使用,这种架构会让你的 Next.js 应用更快、更安全、更易维护——这对现代 React 开发来说是一次真正的胜利。

Back to Blog

相关文章

阅读更多 »

Web生态系统内战

markdown !Javadhttps://media2.dev.to/dynamic/image/width=50,height=50,fit=cover,gravity=auto,format=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads...

React 编码挑战:卡片翻转游戏

React 卡片翻转游戏 – 代码 tsx import './styles.css'; import React, { useState, useEffect } from 'react'; const values = 1, 2, 3, 4, 5; type Card = { id: numb...