跳到内容

10

服务端和客户端组件

要理解服务端和客户端组件的工作原理,熟悉以下两个基本的 Web 概念会很有帮助:

  • 你的应用程序代码可以执行的环境:服务端和客户端。
  • 分隔服务端和客户端代码的网络边界

服务端和客户端环境

在 Web 应用程序的上下文中:

Diagram showing a browser on the left and a server on the right, separated by a network boundary.
  • 客户端指的是用户设备上的浏览器,它向服务器发送请求以获取你的应用程序代码。然后,它将从服务器接收到的响应转换为用户可以交互的界面。
  • 服务端指的是数据中心中的计算机,它存储你的应用程序代码,接收来自客户端的请求,执行一些计算,并发送回适当的响应。

每个环境都有自己的一组功能和约束。例如,通过将渲染和数据获取移至服务器,你可以减少发送到客户端的代码量,从而提高应用程序的性能。但是,正如你之前了解到的,为了使你的 UI 具有交互性,你需要更新客户端上的 DOM。

因此,你为服务端和客户端编写的代码并不总是相同的。某些操作(例如,数据获取或管理用户状态)更适合在某个环境中执行。

网络边界

网络边界是一条概念性的线,它分隔了不同的环境。

在 React 中,你可以选择将网络边界放置在组件树中的位置。例如,你可以在服务端获取数据并渲染用户的帖子(使用服务端组件),然后在客户端上为每个帖子渲染交互式的 LikeButton(使用客户端组件)。

类似地,你可以创建一个在服务端渲染并在页面之间共享的 Nav 组件,但是如果你想显示链接的活动状态,你可以在客户端上渲染 Links 列表。

A component tree showing a layout that has 3 components as its children: Nav, Page, and Footer. The page component has 2 children: Posts and LikeButton. The Posts component is rendered on the server, and the LikeButton component is rendered on the client.

在幕后,组件被拆分为两个模块图。服务端模块图(或树)包含所有在服务端渲染的服务端组件,而客户端模块图(或树)包含所有客户端组件。

在服务端组件渲染完成后,一种称为 React 服务端组件有效负载 (RSC) 的特殊数据格式被发送到客户端。RSC 有效负载包含:

  1. 服务端组件的渲染结果。
  2. 客户端组件应渲染位置的占位符(或空位)以及对其 JavaScript 文件的引用。

React 使用此信息来整合服务端和客户端组件,并更新客户端上的 DOM。

让我们看看这是如何工作的。

使用客户端组件

正如你在上一章中学到的,Next.js 默认使用服务端组件 - 这是为了提高应用程序的性能,意味着你无需采取额外的步骤来采用它们。

回顾浏览器中的错误,Next.js 警告你正在尝试在服务端组件内部使用 useState。你可以通过将交互式的“点赞”按钮移动到客户端组件来修复此问题。

app 文件夹内创建一个名为 like-button.js 的新文件,该文件导出一个 LikeButton 组件

/app/like-button.js
export default function LikeButton() {}

<button> 元素和 handleClick() 函数从 page.js 移动到你的新 LikeButton 组件

/app/like-button.js
export default function LikeButton() {
  function handleClick() {
    setLikes(likes + 1);
  }
 
  return <button onClick={handleClick}>Like ({likes})</button>;
}

接下来,移动 likes 状态和导入

/app/like-button.js
import { useState } from 'react';
 
export default function LikeButton() {
  const [likes, setLikes] = useState(0);
 
  function handleClick() {
    setLikes(likes + 1);
  }
 
  return <button onClick={handleClick}>Like ({likes})</button>;
}

现在,要使 LikeButton 成为客户端组件,请在文件顶部添加 React 'use client' 指令。这告诉 React 在客户端上渲染该组件。

/app/like-button.js
'use client';
 
import { useState } from 'react';
 
export default function LikeButton() {
  const [likes, setLikes] = useState(0);
 
  function handleClick() {
    setLikes(likes + 1);
  }
 
  return <button onClick={handleClick}>Like ({likes})</button>;
}

回到你的 page.js 文件,将 LikeButton 组件导入到你的页面中

/app/page.js
import LikeButton from './like-button';
 
function Header({ title }) {
  return <h1>{title ? title : 'Default title'}</h1>;
}
 
export default function HomePage() {
  const names = ['Ada Lovelace', 'Grace Hopper', 'Margaret Hamilton'];
 
  return (
    <div>
      <Header title="Develop. Preview. Ship." />
      <ul>
        {names.map((name) => (
          <li key={name}>{name}</li>
        ))}
      </ul>
      <LikeButton />
    </div>
  );
}

保存这两个文件并在浏览器中查看你的应用程序。现在没有错误了,一旦你进行更改并保存,你应该会注意到浏览器自动更新以反映更改。

此功能称为 快速刷新。它可以让你即时反馈你所做的任何编辑,并且已预先配置在 Next.js 中。

总结

回顾一下,你了解了服务端和客户端环境以及何时使用每个环境。你还了解到 Next.js 默认使用 React 服务端组件来提高性能,以及如何选择客户端组件来使你的 UI 的较小部分具有交互性。

拓展阅读

关于服务端和客户端组件,还有很多内容可以学习。以下是一些额外的资源:

你已完成章节10

你已经学习了如何使用服务端和客户端组件。

下一步

11: 后续步骤

接下来是什么?