跳到主要内容

渲染模式与数据获取

服务端组件与客户端组件

  • 服务端组件(RSC):默认在服务端渲染,不携带客户端 JS,性能更好。
  • 客户端组件:在文件首行加入 "use client",用于事件与交互。
// 服务端组件(默认)
export default async function Page() {
const data = await fetch("https://api.example.com").then(r => r.json());
return <pre>{JSON.stringify(data, null, 2)}</pre>;
}

// 客户端组件
"use client";
export function Counter() {
const [n, setN] = useState(0);
return <button onClick={() => setN(n + 1)}>{n}</button>;
}

渲染策略

  • SSR(服务端渲染):请求时生成 HTML,适合需要新鲜数据的页面。
  • SSG(静态生成):构建时生成 HTML,适合内容稳定的页面。
  • ISR(增量静态再生成):静态页面按间隔重新验证并更新。
  • PPR(部分预渲染):预渲染静态外壳,动态数据以流方式增量填充。

增量静态再生成(ISR)

  • 在路由段或页面中设置 revalidate:
// app/products/page.tsx
export const revalidate = 60; // 秒
export default async function Products() {
const res = await fetch("https://api.example.com/products", { next: { revalidate: 60 } });
const list = await res.json();
return <ul>{list.map((p: any) => <li key={p.id}>{p.name}</li>)}</ul>;
}

动态路由的静态生成

  • 提供预生成参数:
// app/blog/[slug]/page.tsx
export async function generateStaticParams() {
const slugs = await fetch("https://api.example.com/blog-slugs").then(r => r.json());
return slugs.map((slug: string) => ({ slug }));
}

控制动态与静态行为

  • 通过 dynamic 指定渲染策略:
export const dynamic = "force-static";   // 强制静态
// 或
export const dynamic = "force-dynamic"; // 强制动态(禁用缓存)

PPR(部分预渲染)思路

  • 页面分为静态外壳与动态数据区块。
  • 静态部分在构建或边缘预渲染,动态部分通过流(streaming)逐步填充。
  • 优势:首屏快速、交互快,同时保留动态能力。

错误与状态处理

  • notFound():返回 404
  • redirect():服务端重定向到特定路由
  • 通过 reserved error.js/not-found.js 统一处理。
import { notFound, redirect } from "next/navigation";
export default async function Page({ params }: { params: { id: string } }) {
const res = await fetch(`https://api.example.com/item/${params.id}`, { cache: "no-store" });
if (res.status === 404) notFound();
const item = await res.json();
if (!item.active) redirect("/items");
return <div>{item.name}</div>;
}

推荐实践

  • 默认使用服务端组件与 fetch(带缓存控制),仅在需要交互时使用客户端组件。
  • 为动态路由提供 generateStaticParams,提高预渲染与 SEO。
  • 正确设置 revalidate,平衡数据新鲜度与性能。