从0死磕全栈之Next.js 缓存与数据重新验证

在现代 Web 应用中,性能和数据新鲜度是两个关键指标。Next.js 提供了强大的缓存(Caching)与重新验证(Revalidation)机制,帮助开发者在提升性能的同时,确保用户获取到最新数据。本文将深入介绍 Next.js App Router 中的缓存策略及其使用方式。


1. 什么是缓存?

缓存是一种将计算结果(如 API 请求、数据库查询)临时存储起来的技术。当下次请求相同数据时,可以直接从缓存中读取,避免重复计算,从而显著提升响应速度和降低服务器负载。

在 Next.js 中,缓存不仅适用于数据请求,也适用于页面渲染结果。


2. fetch 的缓存与重新验证

默认行为

在 Next.js 的 App Router 中,fetch 请求默认不会被缓存 。但值得注意的是,即使 fetch 不缓存,Next.js 仍会对包含 fetch 的页面进行静态预渲染(Prerendering),并将整个 HTML 页面缓存下来。

强制缓存

你可以通过设置 cache: 'force-cache' 来显式启用缓存:

ts 复制代码
export default async function Page() {
  const data = await fetch('https://api.example.com/data', {
    cache: 'force-cache', // 启用缓存
  });
  return <div>{data.title}</div>;
}

该请求的结果将被缓存,并在后续请求中直接返回。

基于时间的重新验证(Time-based Revalidation)

为了让缓存数据保持"新鲜",Next.js 支持基于时间的重新验证:

ts 复制代码
export default async function Page() {
  const data = await fetch('https://api.example.com/data', {
    next: { revalidate: 3600 }, // 每小时重新验证一次
  });
}

这意味着:缓存最多保留 3600 秒(1 小时),之后下一次请求会触发重新获取数据,并更新缓存。


3. unstable_cache:缓存任意异步函数

除了 fetch,你可能还需要缓存数据库查询、复杂计算等操作。Next.js 提供了 unstable_cache API(注意:前缀 unstable_ 表示该 API 可能在未来变更)。

基本用法

ts 复制代码
import { unstable_cache } from 'next/cache';
import { getUserById } from '@/lib/data';

export default async function Page({ params }: { params: Promise<{ userId: string }> }) {
  const { userId } = await params;

  const getCachedUser = unstable_cache(
    async () => {
      return getUserById(userId);
    },
    [userId] // 缓存键:确保不同用户的数据独立缓存
  );

  const user = await getCachedUser();
  return <div>{user.name}</div>;
}

配置重新验证与标签

你可以通过第三个参数配置缓存策略:

ts 复制代码
const getCachedUser = unstable_cache(
  async () => getUserById(userId),
  [userId],
  {
    tags: ['user'],      // 用于后续按标签清除缓存
    revalidate: 3600,    // 1 小时后重新验证
  }
);

4. 按标签重新验证:revalidateTag

当你更新了用户数据,希望清除相关缓存时,可以使用 revalidateTag

步骤一:为缓存打标签

无论是 fetch 还是 unstable_cache,都可以添加 tags

ts 复制代码
// 使用 fetch 打标签
await fetch('https://api.example.com/user', {
  next: { tags: ['user'] }
});

// 或使用 unstable_cache 打标签
const getUser = unstable_cache(
  async (id) => db.user.find(id),
  ['user'],
  { tags: ['user'] }
);

步骤二:在更新操作中触发重新验证

通常在 Route HandlerServer Action 中调用:

ts 复制代码
import { revalidateTag } from 'next/cache';

export async function updateUser(id: string, data: any) {
  await db.user.update(id, data);
  revalidateTag('user'); // 清除所有带 'user' 标签的缓存
}

✅ 优势:一个标签可关联多个缓存项,实现批量失效。


5. 按路径重新验证:revalidatePath

如果你只想重新验证某个页面(及其数据),可以使用 revalidatePath

ts 复制代码
import { revalidatePath } from 'next/cache';

export async function createPost(formData: FormData) {
  await savePost(formData);
  revalidatePath('/blog'); // 重新生成 /blog 页面
}

这会触发 Next.js 重新预渲染指定路径,并更新其缓存的 HTML 和数据。


6. 缓存策略对比

方法 适用场景 是否支持 revalidate 是否支持标签 是否适用于非 fetch
fetch + cache API 请求缓存 ✅(next.revalidate ✅(next.tags
unstable_cache 数据库查询、自定义函数缓存
revalidateTag 按标签批量清除缓存 --- ---
revalidatePath 重新生成特定页面 --- ---

7. 最佳实践

  • 优先使用 fetch 缓存 :对于外部 API,直接使用 fetch 的缓存选项最简单高效。
  • 敏感数据避免缓存 :如用户个人资料、支付信息等,应设为动态请求(cache: 'no-store')。
  • 合理设置 revalidate 时间:高频更新的数据设短时间(如 60 秒),静态内容可设更长。
  • 善用标签管理缓存 :通过 tags 实现精准、高效的缓存失效。
  • 结合 Server Actions 使用 :在用户操作(如提交表单)后立即触发 revalidateTagrevalidatePath,实现"即时更新"。

结语

Next.js 的缓存系统兼顾了性能灵活性 ,通过 fetchunstable_cacherevalidateTagrevalidatePath 等 API,开发者可以构建既快速又实时的应用。掌握这些机制,是构建高性能 Next.js 应用的关键一步。

相关推荐
kyriewen7 小时前
我手写了一个 EventEmitter,面试官追问了 6 个问题——第 4 个我没答上来
前端·javascript·面试
IT_陈寒7 小时前
Java的Date类又坑了我一次,改用时间戳真香
前端·人工智能·后端
小林攻城狮8 小时前
使用 Transport 节流解决 Vercel AI SDK 流式渲染卡死问题
前端·react.js
前端缘梦8 小时前
告别 TS 运行时类型漏洞!Zod 完整入门实战教程(前端 / 全栈必备)
前端·react.js·全栈
the_answer8 小时前
Webpack vs Vite 深度对比分析
前端·webpack
转转技术团队8 小时前
验证码识别实战:前端不写页面,改训模型了?
前端
MomentYY8 小时前
Temperature:AI 的“脑洞旋钮”
前端·llm·ai编程
远航_9 小时前
OpenSpec 完整详细介绍
前端·后端
召钱熏9 小时前
状态枚举正确≠渲染正确:一个语音按钮的状态机边界修复实录
android·前端
SkyWalking中文站9 小时前
认识 Horizon UI · 1/17:SkyWalking 新一代可观测性控制台
运维·前端·监控