React Server Components (RSC) 与 App Router 简介:Next.js 的未来范式
React Server Components (RSC) 与 App Router 简介:Next.js 的未来范式
作者:码力无边
在过去的十八篇文章中,我们已经深入掌握了 Next.js 的 pages
目录(Pages Router)模型。我们学会了如何使用 getStaticProps
, getServerSideProps
等数据获取函数,以及如何构建 API 路由和中间件。这是一个成熟、稳定且强大的模型,支撑了无数生产级应用。
然而,Web 开发的世界永远在进化。为了追求更优的性能、更佳的开发体验和更灵活的架构,React 核心团队与 Next.js 团队 (Vercel) 联手,推出了一项堪称革命性的新特性——React Server Components (RSC),并围绕它构建了全新的路由和渲染模型:App Router。
从 Next.js 13 开始,App Router 成为了推荐的开发方式(pages
目录依然完全支持)。理解这个新范式,不仅是跟上 Next.js 的发展,更是洞察整个前端开发未来的关键。本文将为你揭开 RSC 和 App Router 的神秘面纱,解释它们是什么,以及它们带来了哪些颠覆性的优势。
回顾过去:pages
目录的局限性
在我们拥抱未来之前,先要理解它解决了过去的什么问题。pages
目录模型虽然优秀,但存在一些固有的局限性:
- 数据获取的“全有或全无”:在一个页面中,你只能选择一种主要的数据获取策略(SSG 或 SSR)。如果页面上的大部分内容是静态的,但有一个小组件需要动态数据,你往往不得不让整个页面都采用 SSR (
getServerSideProps
),从而牺牲了性能。 - 客户端 JavaScript 的瀑布流:在
pages
目录中,一个路由下的所有组件代码,最终都会被打包发送到客户端(即使它们是在服务器上预渲染的)。当组件树变得庞大时,客户端需要下载和解析的 JavaScript 也会随之增多。 - 布局(Layouts)的实现不够原生:虽然我们通过
_app.tsx
和getLayout
模式实现了布局,但这更像是一种“变通方案 (workaround)”,而不是框架原生支持的一等公民。
新范式:React Server Components (RSC)
RSC 是理解 App Router 的核心。它引入了一种全新的组件类型,从根本上改变了我们对“组件”的认知。
在此之前,我们所写的所有 React 组件,无论是否经过 SSR/SSG,最终都会在客户端“激活”(hydrate),成为客户端组件 (Client Components)。它们可以使用状态 (useState
)、生命周期 (useEffect
),并响应用户交互。
React Server Components 是一种只在服务器上运行、且永远不会被发送到客户端的组件。
Server Components 的超能力
-
直接访问后端资源:由于它们只在服务器上运行,Server Components 可以像你的后端代码一样,直接、安全地访问数据库、文件系统、内部服务或使用私有环境变量。你不再需要为了获取数据而创建一个专门的 API 路由。
// app/page.tsx (这是一个 Server Component) import { db } from '@/lib/db'; // 直接导入数据库实例async function getPosts() {// 直接进行数据库查询return await db.post.findMany(); }export default async function HomePage() {const posts = await getPosts();return (<main>{posts.map(post => <PostItem key={post.id} post={post} />)}</main>); }
注意到这里的
async/await
了吗?Server Components 可以是async
函数!这使得数据获取变得前所未有的直观。 -
零客户端 JavaScript:Server Components 的代码及其所有依赖(包括大型库,如
marked
用于解析 Markdown)都不会被打包进客户端的 JavaScript bundle 中。它们在服务器上渲染成一种特殊的中间格式,然后流式传输到客户端。这能极大地减小客户端 JavaScript 的体积,显著提升应用的初始加载性能。 -
自动代码分割:每个 Server Component 都可以被看作是一个代码分割点,实现了组件级别的代码分割。
客户端组件 (Client Components)
当然,我们仍然需要能够响应用户交互、使用状态和生命周期的组件。在 App Router 中,这类组件被称为客户端组件 (Client Components)。
你需要通过在文件顶部添加一个 "use client";
指令来明确地将一个组件标记为客户端组件。
// components/Counter.tsx
"use client"; // 标记为客户端组件import { useState } from 'react';export default function Counter() {const [count, setCount] = useState(0);return (<button onClick={() => setCount(count + 1)}>You clicked {count} times</button>);
}
重要规则:
- 默认情况下,App Router (
app
目录) 中的所有组件都是 Server Components。 - 只有需要使用 React Hooks(如
useState
,useEffect
)或浏览器 API(如window
)时,才需要使用"use client";
将其标记为客户端组件。
App Router:为 RSC 量身打造的路由系统
App Router 是一个基于 RSC 构建的全新路由系统,它位于项目的 app
目录中。
核心特性:
- 默认 Server Components:如上所述,
app
目录下的组件默认都是 Server Components,鼓励你将尽可能多的逻辑保留在服务器端。 - 原生支持布局 (Layouts):App Router 将布局提升为一等公民。你可以在任何目录下创建一个
layout.tsx
文件,它会自动包裹该目录及其所有子目录下的页面。这使得创建复杂的嵌套布局变得极其简单和直观。 - 组件化的数据获取:数据获取不再局限于页面级别。任何一个 Server Component 都可以独立地获取自己的数据。这解决了
pages
目录中“全有或全无”的问题,让数据获取更加精细化。 - 流式渲染 (Streaming):App Router 与 React Suspense 深度集成,支持服务端流式渲染。服务器可以先快速发送页面的静态部分(如布局),然后随着 Server Components 数据获取的完成,逐步将内容“流”到客户端 UI 中,用户可以更快地看到页面的部分内容,而不是等待整个页面加载完成。
app
目录结构示例
app/
├── layout.tsx # 根布局
├── page.tsx # 首页 (/)
├── dashboard/
│ ├── layout.tsx # Dashboard 区域的专属布局
│ ├── page.tsx # /dashboard 页
│ └── settings/
│ ├── page.tsx # /dashboard/settings 页
└── ...
这种结构比 pages
目录更具表现力和组织性。
总结:一次思维的转变
从 Pages Router 迁移到 App Router,不仅仅是学习一套新的 API,更是一次思维模式的深刻转变。
传统思维 (Pages Router) | 新范式思维 (App Router) |
---|---|
组件默认在客户端运行。 | 组件默认在服务器运行。 |
数据获取在页面级别进行。 | 数据获取在组件级别进行。 |
整个页面的 JS 被发送到客户端。 | 只有客户端组件的 JS 被发送。 |
布局是“变通方案”。 | 布局是原生一等公民。 |
React Server Components 和 App Router 代表了 React 和 Next.js 的未来。它们通过将更多的计算工作移回服务器,并以更智能的方式向客户端交付内容,旨在从根本上解决现代 Web 应用面临的性能和复杂性挑战。
这只是一个开始。在接下来的文章中,我们将深入探讨 App Router 和 Pages Router 的具体差异和选择考量,并逐步学习如何在 App Router 中进行数据获取、状态管理和构建复杂的应用。这次范式转移是激动人心的,让我们一起拥抱它!