Corey Rosamond · 2026/3/1 · 约 4 分钟阅读
本文为英文原文的简体中文译文。资料和事件日期保留原文发表时的语境;代码示例保持原样。 Read in English

使用 App Router
网站开发方式已经发生很大变化。Next.js 15 的 App Router 与 React Server Components 不只是小幅改进,也改变了构建应用时对服务器与浏览器职责的理解。
本站使用 Next.js 15 App Router。以下示例用于解释渲染模型,并非性能基准,也不保证更换路由器就能改善所有应用。
React Server Components:核心变化
React Server Components(RSC)让组件完全在服务器上渲染,其自身的 JavaScript 不发送到客户端。这与传统 SSR 有区别。
// This component runs on the server. Interactive descendants may still need client JavaScript.
export async function RecentProjects() {
const projects = await db.project.findMany({
where: { published: true },
orderBy: { createdAt: 'desc' },
take: 6,
});
return (
<section>
{projects.map((project) => (
<ProjectCard key={project.id} project={project} />
))}
</section>
);
}
关键是:在消费数据的位置获取数据。不必再从 getServerSideProps 层层传递属性,也能减少为服务端数据设置全局状态的需要。
默认使用 Server Components。需要浏览器 API、状态或事件处理器时再加入
"use client"。应用中的大量内容可以继续在服务端渲染。
App Router 架构
App Router 采用基于文件系统的路由和一组约定。
布局与模板
布局在导航之间保留,保存状态并减少不必要的重新渲染。模板在导航时重新挂载,适合进入动画等场景。
src/app/
├── layout.tsx # Root layout (shared header, footer)
├── template.tsx # Page transition wrapper
├── page.tsx # Homepage
├── blog/
│ ├── layout.tsx # Blog-specific layout (sidebar TOC)
│ ├── page.tsx # Blog index
│ └── [slug]/
│ └── page.tsx # Individual blog posts
动态数据与静态生成
generateStaticParams 提供构建时生成页面所需的动态路由参数;实际行为仍取决于该路由的渲染和缓存选择。generateMetadata 提供逐页元数据:
export async function generateStaticParams() {
const posts = getAllPosts();
return posts.map((post) => ({ slug: post.slug }));
}
export async function generateMetadata({ params }: Props): Promise<Metadata> {
const { slug } = await params;
const post = getPostBySlug(slug);
if (!post) return {};
return {
title: post.frontmatter.title,
description: post.frontmatter.description,
};
}
测量您正在改变的边界
在服务端与客户端之间移动功能之前,记录 JavaScript 体积、请求顺序、渲染时间和交互行为。变更后重复相同流程。将依赖移出客户端依赖图可能减少下载,但缓慢数据库查询或未缓存的服务端工作仍可能拖慢页面。
Next.js 15 组件文档解释了不同组件在哪里运行,以及初始页面如何变得可交互。
流式渲染与 Suspense
Next.js 15 使用 React Suspense 向浏览器流式发送 HTML,让较慢组件不必阻塞整个页面:
import { Suspense } from 'react';
export default function DashboardPage() {
return (
<div>
<h1>Dashboard</h1>
<Suspense fallback={<MetricsSkeleton />}>
<AnalyticsMetrics />
</Suspense>
<Suspense fallback={<ActivitySkeleton />}>
<RecentActivity />
</Suspense>
</div>
);
}
Suspense 允许已经准备好的部分先显示,较慢区域随后完成。时间仍取决于边界之前的工作、服务器与网络。选择有用的占位内容,并在真实延迟条件下测试加载体验。
Server Actions:通过服务端函数修改数据
Server Actions 让表单处理可以由一个在服务器运行的函数完成:
'use server';
export async function submitContactForm(formData: FormData) {
// Import the same schema used by the form; do not trust type assertions.
const { name, email, message } = contactSchema.parse({
name: formData.get('name'),
email: formData.get('email'),
message: formData.get('message'),
});
// Apply authorization and abuse controls appropriate to the operation.
await db.contactSubmission.create({
data: { name, email, message },
});
await sendNotificationEmail({ name, email, message });
revalidatePath('/admin/submissions');
}
在 Server Components 中使用 Server Actions 的表单支持渐进增强。必须验证输入,并分别处理数据修改与通知失败,避免重试导致重复保存。
选择适合的方式
用渲染模型简化具体数据流或交互。保持客户端边界精简,设计加载和错误状态,并使用接近生产的数据验证结果。新部署应检查仍受支持的版本及安全更新,不要只凭文章标题选版本。
准备改进现有网站应用?联系我们,讨论合适的 Next.js 架构如何改善产品性能与使用体验。