2.1 数据获取 Data Fetching


2.1 数据获取(Data Fetching)

本节摘要:数据是页面的灵魂,但"数据在何时、何地获取"决定了页面的性能与 SEO。本节讲清四种数据获取策略(CSR/SSR/SSG/ISR)的原理、实现与选择逻辑,重点掌握服务器组件里的 async 取数与 Next.js 的缓存模型。

核心问题

阅读完本节,你应当能够:

  1. 解释四种数据获取策略的原理
  2. 在服务器组件中用 async 直接取数
  3. 用 revalidate 实现 ISR(增量静态再生)
  4. 按业务场景选择合适的获取策略
  5. 理解静态渲染与动态渲染的默认行为

问题与直觉:数据在哪获取,决定了页面多快

同一个页面,数据获取方式不同,体验天差地别:

  • 构建时获取(SSG):页面秒开、SEO 完美,但数据是"快照";
  • 请求时获取(SSR):每次请求都是最新数据,但服务器要实时渲染;
  • 浏览器获取(CSR):交互最强,但首屏白屏、SEO 弱;
  • 定时更新(ISR):静态的快 + 定期刷新的新。

直觉类比:数据获取像"餐厅备菜"——SSG 是"提前把菜全部做好放展示柜"(顾客来了直接端,但菜是提前的);SSR 是"顾客点单现炒"(最新鲜但慢);ISR 是"主菜提前做,每隔几小时重做一轮"(兼顾快与新)。

💡 关键直觉:Next.js 的默认姿势是"能静态就静态"。服务器组件默认在构建时渲染成静态页面;只有你主动标记(动态 API、cookies、请求头等)才变成动态。这个"静态优先"的默认值,是 Next.js 性能好的根本原因。

核心原理:四种策略

2.1 服务器组件中的 async 取数

App Router 中,服务器组件可以直接 async + await 取数,不用 useEffect、不用 fetch 包装:

// app/posts/page.tsx —— 默认静态渲染(SSG) export default async function PostsPage() { const res = await fetch("https://api.example.com/posts", { cache: "force-cache", // 默认:构建时取一次,静态缓存 }); const posts = await res.json(); return ( <ul> {posts.map((p) => <li key={p.id}>{p.title}</li>)} </ul> ); }

2.2 fetch 的缓存选项(Next.js 缓存模型)

选项 行为 渲染方式
cache: "force-cache"(默认) 构建时取一次,静态缓存 静态(SSG)
cache: "no-store" 每次请求都重新取 动态(SSR)
next: { revalidate: 60 } 缓存 60 秒后后台刷新 静态 + ISR
// ISR:每 60 秒重新验证一次 export default async function Page() { const res = await fetch("https://api.example.com/posts", { next: { revalidate: 60 }, // 60 秒增量再生 }); const posts = await res.json(); return <ul>{posts.map((p) => <li key={p.id}>{p.title}</li>)}</ul>; }

2.3 不用 fetch 时的控制:dynamic

非 fetch 取数(如直接查数据库)用路由段配置控制:

// app/dashboard/page.tsx export const dynamic = "force-dynamic"; // 强制动态渲染(SSR) // 或 revalidate export const revalidate = 60; // ISR:60 秒

2.4 客户端取数(CSR)

交互性强的组件(需要 loading、轮询、用户触发刷新)用客户端取数:

"use client"; import { useEffect, useState } from "react"; export function LiveData() { const [data, setData] = useState(null); useEffect(() => { fetch("/api/stats").then((r) => r.json()).then(setData); }, []); return <div>{data ? data.value : "加载中..."}</div>; }

工程实践要点:策略选择决策树

三、工程实践要点:策略选择决策树

实战经验

  1. 能 SSG 就 SSG:内容型页面默认静态,性能最好;
  2. ISR 是"免费午餐":静态的快 + 定期刷新,新闻/榜单类首选;
  3. SSR 留给"必须最新":登录用户数据、实时库存;
  4. CSR 留给"组件级交互":图表、实时监控、轮询;
  5. 混合使用:一个页面可以是"静态骨架 + 客户端局部数据"。

常见误区与排查

误区 现象 正解
所有页面都实时取数 服务器压力大、页面慢 按策略选择,默认静态
以为服务器组件不能取数 还在用 useEffect 取数 async 直接 await
fetch 缓存不生效 页面还是动态 检查 cache 选项与动态 API 使用
改数据页面不更新 静态缓存 ISR revalidate 或强制动态
忘记 dynamic 配置 数据库数据不刷新 用 revalidate 或 force-dynamic

动手演练:三种页面对比

// 1. SSG:app/blog/page.tsx(默认静态) export default async function Blog() { const res = await fetch("https://jsonplaceholder.typicode.com/posts"); const posts = await res.json(); return <ul>{posts.map((p) => <li key={p.id}>{p.title}</li>)}</ul>; } // 2. ISR:app/news/page.tsx(60 秒刷新) export const revalidate = 60; // 3. SSR:app/profile/page.tsx(每次请求实时) export const dynamic = "force-dynamic";

运行 npm run build,观察三个页面的构建标记:博客是 (静态)、新闻是 (静态+ISR)、个人页是 ƒ(动态)——用构建输出验证你的策略选择是否正确

本章回顾

  • 四种策略:SSG(构建时)、SSR(请求时)、ISR(定时刷新)、CSR(浏览器)。
  • 服务器组件 async:直接 await 取数,无需 useEffect。
  • fetch 缓存:force-cache(静态默认)、no-store(动态)、revalidate(ISR)。
  • 路由段配置:dynamic 与 revalidate 控制非 fetch 取数。
  • 选择逻辑:能静态就静态,ISR 是性能与新度的平衡,SSR 留给必须最新,CSR 留给组件交互。
  • 验证方式:build 输出的 ○ / ƒ 标记就是策略的答案。

深入理解:缓存失效与并行取数

数据获取的进阶要点

缓存失效:ISR 的 revalidate 是"时间驱动"的(到时间后台刷新)。需要"事件驱动"失效(数据变了立即刷新)时,配合 Server Actions 的 revalidatePath 或 revalidateTag:

// 数据变更后立即刷新相关页面 "use server"; export async function updatePost(id: string) { await prisma.post.update({ where: { id }, data: { ... } }); revalidatePath("/posts"); // 立即让 /posts 重新生成 revalidatePath(`/posts/${id}`); }

并行取数:页面有多个独立数据时,用 Promise.all 并行(比串行快):

export default async function DashboardPage() { // 串行:慢(先等 A 再等 B) // const a = await getA(); const b = await getB(); // 并行:快(A、B 同时进行) const [a, b] = await Promise.all([getA(), getB()]); return <Dashboard a={a} b={b} />; }

数据获取决策流程

一句话总结:数据获取没有"最好",只有"最适合"——按 SEO 需求与变化频率选择,是数据层设计的第一原则。

常见问题速答

问:服务器组件里能同时用 fetch 和查数据库吗?
能。fetch 外部 API 与直接查数据库(Prisma)都支持,按需选择。注意:fetch 的缓存选项只对 fetch 生效,数据库查询用 dynamic/revalidate 路由段配置控制。

问:revalidate 后数据什么时候更新?
revalidate 是"后台刷新":缓存过期后,下一次请求触发后台重新生成,用户拿到的是旧缓存,生成完成后新请求拿新数据。它不是"到期立即删缓存",而是"到期后下一次请求刷新"。

问:如何强制刷新某个页面?
开发中想立即看到最新数据:Server Action 里调 revalidatePath;或临时把 dynamic 设为 force-dynamic;生产环境等待 revalidate 自然过期。

问:客户端组件里还能用 fetch 吗?
能,但注意:客户端 fetch 不走 Next.js 的缓存模型(服务端 fetch 才有 cache 选项),且会造成"先加载后取数"的体验。优先服务器组件取数。

一句话总结

数据获取的核心心法是"能静态就静态,能缓存就缓存"——SSG 构建时生成、ISR 定时刷新、SSR 请求时渲染、CSR 浏览器取数,按 SEO 需求与数据变化频率选择。记住构建输出里的 ○ 和 ƒ 标记,它们是策略选择的直观验证。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U