Join the discussion
Comments
Loading comments...
Cookie preferences
We use essential cookies for sign-in and preferences. With your permission, we also use basic analytics to improve lessons.
Join the discussion
Loading comments...
We use essential cookies for sign-in and preferences. With your permission, we also use basic analytics to improve lessons.
服务端/客户端边界、RSC 负载流式传输和零打包的服务端渲染。
React 服务端组件(RSC)是仅在服务器上运行的组件 — 它们永远不会发送到浏览器。它们可以直接访问数据库、文件系统和仅服务器端的秘密,客户端打包成本为零。RSC 输出不是 HTML,而是一种特殊的有线格式(RSC Payload),客户端的 React 使用它来重建组件树,而无需重新运行服务器代码。
页面加载时,服务器运行你的服务端组件,直接执行异步数据库查询、文件读取或 API 调用。输出不是 HTML — 而是自定义流式格式的序列化 React 元素树(RSC Payload)。
当服务器遇到客户端组件(用 'use client' 标记)时,它不渲染它。相反,它在 payload 中插入 CLIENT_REFERENCE 占位符,包含组件的模块 URL 和 props。客户端通过在浏览器中运行客户端组件来填充这些空洞。
服务器随着每个异步操作完成流式传输 RSC payload。客户端的 React 运行时接收块并逐步构建组件树 — 先是服务端组件输出,然后是客户端组件外壳,然后是水合。
客户端的 React 获取流式传输的 RSC payload 并使用它重建完整的组件树。只有客户端组件被水合(附加事件监听器)。服务端组件输出已经是最终的 — 不在客户端重新执行。
用 'use server' 标记的函数变成 Server Actions — 客户端可以像普通异步函数一样调用的 RPC 风格函数。在底层,React 向服务器发送一个 POST 请求,带有编码的参数,并流式返回新的 RSC payload。
文件级指令,将模块及其所有导入标记为客户端组件。创建客户端边界 — 所有导出都可以使用 hooks、事件处理器和浏览器 API。
将函数标记为 Server Action。可以通过网络请求从客户端组件调用。该函数在服务器上以完整的服务器端访问权限运行。
描述服务端渲染组件树的紧凑流式 JSON 格式。包含客户端组件应该放置位置的 CLIENT_REFERENCE 空洞。不是 HTML。
服务端组件永远不会出现在客户端 JS 包中。导入 500KB markdown 解析器的服务端组件向客户端添加 0 字节。
没有 'use client' 或 'use server' 的组件可以在两种上下文中使用。当被服务端组件导入时在服务器运行,当被客户端组件导入时在客户端运行。
1// ServerComponent.tsx — runs ONLY on server2// No bundle cost, can use async/await, access DB directly3async function ProductPage({ id }) {4 const product = await db.products.findById(id); // direct DB access!56 return (7 <div>8 <h1>{product.name}</h1>9 {/* Pass server data to a client component */}10 <AddToCartButton productId={product.id} price={product.price} />11 </div>12 );13}1415// AddToCartButton.tsx — runs on client (needs onClick)16'use client';17function AddToCartButton({ productId, price }) {18 const [added, setAdded] = useState(false);1920 return (21 <button onClick={() => {22 addToCart(productId);23 setAdded(true);24 }}>25 {added ? 'Added!' : `Add for $${price}`}26 </button>27 );28}
服务端组件从根本上改变了性能等式。之前,任何获取数据的组件都必须将获取库、数据转换逻辑和渲染代码发送到客户端。有了 RSC,所有这些都留在服务器上。客户端只接收最终渲染输出加上交互岛屿(客户端组件)。这带来了显著更小的包和更快的可交互时间。
A product detail page needs to show: product info (from DB), reviews (from API), and an interactive 'Add to Cart' button. Traditional approach: fetch all data client-side with useEffect → loading spinners → hydration overhead.
Client-side fetching means: ship the fetch logic in the bundle → render loading state → make API calls from the browser → parse responses → re-render with data. The user sees a spinner, the bundle includes API client code, and there's a waterfall: page loads → JS executes → fetch starts → data arrives.
Make ProductPage and Reviews as Server Components (the default in Next.js App Router). They fetch data on the server with direct DB/API access and send pre-rendered HTML. Only AddToCartButton is a Client Component (marked with 'use client') because it needs onClick. Zero API client code in the bundle.
Takeaway: Server Components eliminate the request waterfall by moving data fetching to the server. Only interactive components (buttons, forms, animations) need to be Client Components. This pattern typically reduces bundle size by 30-50% and eliminates loading spinners for initial data.
You're building a blog post page. It has a header (static), article body (static with syntax-highlighted code), a comment section (interactive — like/reply buttons), and a share widget (interactive — copy link, social share).
New developers often mark the entire page as 'use client' because ONE component needs interactivity. This sends the entire page's JavaScript to the browser — syntax highlighting library, markdown parser, etc. — even though 80% of the page is static content.
Keep the page as a Server Component (default). Only wrap Comment and ShareWidget with 'use client'. The header, article body, and syntax highlighting render on the server and send zero JavaScript. The client only receives the small interactive components. Think of the boundary as: 'What needs browser APIs (state, effects, event handlers)?'
Takeaway: Push the 'use client' boundary as far DOWN the tree as possible. The rule: a component should be a Client Component only if it directly uses useState, useEffect, onClick, or browser-only APIs. Everything else should stay as a Server Component for zero bundle cost.