从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 应用的关键一步。

相关推荐
用户47949283569151 小时前
"讲讲原型链" —— 面试官最爱问的 JavaScript 基础
前端·javascript·面试
用户47949283569151 小时前
2025 年 TC39 都在忙什么?Import Bytes、Iterator Chunking 来了
前端·javascript·面试
大怪v2 小时前
【Virtual World 04】我们的目标,无限宇宙!!
前端·javascript·代码规范
狂炫冰美式2 小时前
不谈技术,搞点文化 🧀 —— 从复活一句明代残诗破局产品迭代
前端·人工智能·后端
xw53 小时前
npm几个实用命令
前端·npm
!win !3 小时前
npm几个实用命令
前端·npm
代码狂想家3 小时前
使用openEuler从零构建用户管理系统Web应用平台
前端
dorisrv4 小时前
优雅的React表单状态管理
前端
蓝瑟5 小时前
告别重复造轮子!业务组件多场景复用实战指南
前端·javascript·设计模式
dorisrv5 小时前
高性能的懒加载与无限滚动实现
前端