Next.js App Router 数据获取:fetch 缓存、revalidate 与 dynamic 一次讲清

Next.js App Router 数据获取:fetch 缓存、revalidate 与 dynamic 一次讲清

从 Pages Router 迁到 App Router 的人,几乎都被同一件事绊倒过:「我明明改了数据库,页面刷新了半天还是旧数据」,或者反过来「本地跑得好好的,一部署到线上数据就不更新了」。根源都在同一处 ------ App Router 把 fetch 悄悄改造了,默认帮你做缓存,而你没意识到。

这篇把 App Router 里数据到底什么时候缓存、什么时候刷新、怎么手动控制讲透,给出能直接照抄的配置。

第一个反直觉:fetch 默认被缓存了

在 App Router 的服务端组件里,Next.js 用自己的实现包了原生 fetch。看这段代码:

tsx 复制代码
// app/products/page.tsx ------ 默认就是服务端组件
export default async function ProductsPage() {
  // 注意:这个请求结果默认会被永久缓存
  const res = await fetch("https://api.example.com/products");
  const products = await res.json();

  return (
    <ul>
      {products.map((p: any) => (
        <li key={p.id}>{p.name}</li>
      ))}
    </ul>
  );
}

在生产构建下,这个 fetch 的结果默认等价于 cache: 'force-cache' ------ 结果会被缓存,后续请求直接命中缓存,哪怕后端数据已经变了页面也不更新。这就是「改了数据库页面还是旧的」的元凶。

提示:Next.js 14 里未配置的 fetch 默认强缓存;从 Next.js 15 起默认改成了不缓存(no-store)。但依赖「默认值」是最坑的写法 ------ 版本一升行为就反转。所以别赌默认值,永远显式声明缓存策略

三种缓存策略,对号入座

fetch 的第二个参数直接决定缓存行为,一共就三种,记住场景就不会错:

tsx 复制代码
// 1) 永久缓存:适合几乎不变的数据,比如文档、配置、静态内容
await fetch(url, { cache: "force-cache" });

// 2) 完全不缓存:每次请求都打后端,适合购物车、实时价格、用户私有数据
await fetch(url, { cache: "no-store" });

// 3) 定时重新验证:缓存 60 秒,过期后台自动刷新,适合博客/商品列表这种"可以旧一会儿"的
await fetch(url, { next: { revalidate: 60 } });

选择标准很简单:

  • 数据几乎不变 → force-cache;
  • 数据必须实时、每次都要最新 → no-store;
  • 数据允许有几十秒延迟,但要兼顾性能 → next: { revalidate: N }

第三种是最常用的,它用的是 stale-while-revalidate 策略:60 秒内直接给缓存(快),过期后第一个访问的人拿到的还是旧数据,但后台会悄悄拉新数据换掉缓存,下一个人就看到新的了。既快又不会太旧。

revalidate 也可以写在页面级别

如果一个页面里有多个 fetch,或者数据来自数据库(不走 fetch),用页面级的 export const revalidate 更省事。它作用于整个路由段:

tsx 复制代码
// app/blog/page.tsx
// 整个页面每 300 秒重新生成一次
export const revalidate = 300;

export default async function BlogPage() {
  const posts = await db.post.findMany();  // 直接查库,不是 fetch
  return <PostList posts={posts} />;
}

revalidate = 300 表示这个页面走 ISR(增量静态再生):构建时生成一次 HTML,之后每 300 秒最多重新生成一次。适合内容更新不频繁的博客、落地页。

dynamic:强制整页动态渲染

有时你想让整个页面每次请求都实时渲染(比如后台管理页、依赖登录态的页面),不管里面 fetch 怎么配。用 export const dynamic:

tsx 复制代码
// app/dashboard/page.tsx
// force-dynamic:每次请求都在服务端实时渲染,禁用一切缓存
export const dynamic = "force-dynamic";

export default async function Dashboard() {
  const stats = await fetch("https://api.example.com/stats", {
    cache: "no-store",
  }).then((r) => r.json());

  return <StatsPanel data={stats} />;
}

dynamic 有几个常用值:

  • 'auto'(默认):Next.js 自己判断能不能静态化;
  • 'force-dynamic':强制每次请求动态渲染,等于给整页所有 fetch 兜底加 no-store;
  • 'force-static':强制静态,即使用了 no-store 也会被当成缓存。

另外一个隐藏规则要记住:只要你在组件里用了 cookies()headers()searchParams 这些运行时才知道的东西,整个路由会自动变成动态渲染 ,不需要手动写 force-dynamic。这也是很多人「以为是静态页,结果每次都在服务端跑」的原因。

按需刷新:数据变了立刻更新,不用等 revalidate

定时刷新有个缺点:用户在后台改了商品,前台要等 60 秒才生效。更好的做法是 ------ 改完数据后主动通知 Next.js「这个缓存失效了」。用 revalidatePathrevalidateTag:

tsx 复制代码
// app/actions.ts
"use server";
import { revalidatePath, revalidateTag } from "next/cache";

export async function updateProduct(id: string, data: FormData) {
  await db.product.update({ where: { id }, data: { /* ... */ } });

  // 方式一:让某个路径的缓存立即失效,下次访问重新生成
  revalidatePath("/products");

  // 方式二:让打了特定 tag 的所有 fetch 失效(更精准)
  revalidateTag("products");
}

配合 revalidateTag 使用时,在 fetch 上打标签:

tsx 复制代码
// 给这个请求打上 'products' 标签
await fetch("https://api.example.com/products", {
  next: { tags: ["products"] },
});

这样你可以设一个很长的 revalidate(甚至永久缓存),平时享受满速缓存,只在真正改数据时用 revalidateTag('products') 精准击穿相关缓存。这是生产环境最推荐的组合:默认缓存 + 写操作后按需失效

小结

  • App Router 里的 fetch 默认带缓存(且默认值随版本变),别赌默认值,永远显式写缓存策略。
  • 三选一:不变用 force-cache,实时用 no-store,能容忍延迟用 next: { revalidate: N }(stale-while-revalidate,又快又够新)。
  • 页面级用 export const revalidate 控制 ISR;用 cookies()/headers() 会自动转动态渲染;export const dynamic = 'force-dynamic' 强制整页实时。
  • 最佳实践:长缓存 + revalidateTag/revalidatePath 按需失效,改完数据立刻生效,平时全走缓存。

一句话记忆:缓存策略要么写在 fetch 上,要么写在页面 export 上,改完数据用 revalidate 系列主动通知失效。

相关推荐
成都渲染101云渲染66665 小时前
如何在3ds Max中实现更快、更高质量的渲染
前端·javascript·人工智能
闲猫7 小时前
Spring AI 对接Deepseek ChatModel 聊天对话
java·前端·spring
自信的未来9 小时前
JSON 工具|Web Worker 工程化打包 + 语法自动修复 + 多语言代码生成实战
java·前端·json
全栈前端老曹9 小时前
【MongoDB】安全与权限管理 —— 用户认证、角色权限、SSL 加密
前端·javascript·数据库·安全·mongodb·nosql·ssl
zhangxingchao10 小时前
AI大模型核心八:从 Agent Skill、长文档 RAG 到知识库更新与训练策略
前端·人工智能·后端
zhangxingchao11 小时前
AI大模型核心七:从 Workflow、RAG、记忆治理到幂等性
前端·人工智能·后端
gyx_这个杀手不太冷静11 小时前
Agent开发进阶指南(第 1 章):AI Agent 到底是什么?
前端·agent·ai编程
小小小小宇12 小时前
模型相关知识
前端
小小小小宇12 小时前
模型相关知识二
前端