Featured image of post 揭秘 Next.js 应用路由器缓存机制Featured image of post 揭秘 Next.js 应用路由器缓存机制

揭秘 Next.js 应用路由器缓存机制

Next.js App Router 缓存层的综合指南:请求记忆、数据缓存、完整路由缓存和路由器缓存。

介绍

Next.js App Router 具有强大的缓存架构,旨在优化加载速度并减少后端 API 开销。

然而,由于 Next.js 集成了多个缓存层,开发人员经常遇到调试问题。常见问题包括页面显示陈旧数据或 API 触发过于频繁。

本文通过解释 Next.js 中的四种缓存机制以及配置、刷新和无效它们的技术来消除混乱。


1. Next.js App Router 的四个缓存层

Next.js App Router 使用四个独立的缓存层:

[Client Side]
  1. Router Cache (Temporary in-browser navigation cache)
[Server Side]
  2. Request Memoization (React component render-cycle memory)
  3. Data Cache (Persistent API request data across users)
  4. Full Route Cache (Static HTML and RSC payloads at build-time)

下面总结一下各层的特点:

缓存层地点缓存内容寿命无效/重新生效
1.路由器缓存客户RSC有效载荷基于会话(分钟)router.refresh(),服务器操作
2.请求记忆服务器fetch 回复单一渲染周期根据请求自动清除
3.数据缓存服务器API响应数据坚持不懈revalidatePathrevalidateTag
4.完整路由缓存服务器HTML 和 RSC 有效负载坚持不懈重建,与数据缓存相关

2. 每个缓存层如何工作

① 请求记忆

如果在单个渲染请求期间在同一 React 组件树中多次获取相同的 URL 和配置,则仅执行第一个请求。其余调用从记忆缓存中读取。

// Component A
async function UserProfile() {
  const res = await fetch('https://api.example.com/user'); // Fires API call
  return <div>...</div>;
}

// Component B (rendered during the same cycle)
async function UserSettings() {
  const res = await fetch('https://api.example.com/user'); // Reads from memoization cache
  return <div>...</div>;
}
  • 好处: 无需通过 props 向下传递数据。组件可以独立获取自己的数据需求。

② 数据缓存

数据缓存在传入请求和多个用户之间保留 API 响应数据。

默认情况下,Next.js 14 中的标准 fetch 请求缓存在此处(注意:Next.js 15 默认为未缓存的获取请求,尽管可以显式启用缓存)。

  • 重新验证: 您可以设置基于时间的 TTL 或触发按需重新验证:
    // Revalidate cache after one hour (3600 seconds)
    fetch('https://api.example.com/products', { next: { revalidate: 3600 } });
    

③ 全路由缓存

在构建时,Next.js 编译页面结构,呈现静态 HTML 和 RSC 有效负载。这对应于静态站点生成 (SSG)。

  • 动态中断: 如果 Next.js 检测到动态函数(例如 cookies()headers() 或读取动态 searchParams),它会绕过完整路由缓存。然后,该页面会根据每个请求动态呈现。

④ 路由器缓存

客户端浏览器缓存。当用户在路线之间导航时,Next.js 会在内存中缓存预取和访问过的页面。这使得即时来回导航成为可能。

  • 无效: 在服务器操作内触发 revalidatePath 会自动使路由器缓存失效,确保用户在下次导航时获得更新的布局。

3. 实用的缓存配置秘诀

场景 A:实时数据(绕过服务器缓存)

对于显示实时更新的页面(例如财务图表或实时状态板),请禁用 fetch 请求的缓存:

const res = await fetch('https://api.example.com/live-ticks', {
  cache: 'no-store'
});

或者,强制整个路线保持动态:

export const dynamic = 'force-dynamic';

场景 B:按需重新验证 (revalidateTag)

对于在数据库记录更改(例如发布博客文章)时立即更新缓存内容非常有用。

// Append a tag to the fetch cache
const res = await fetch('https://api.example.com/posts', {
  next: { tags: ['blog-posts'] }
});

// Trigger cache invalidation in a Server Action
import { revalidateTag } from 'next/cache';

async function updatePost() {
  'use server';
  await saveToDatabase();
  // Clear all caches tagged with 'blog-posts'
  revalidateTag('blog-posts');
}

结论

了解 Next.js 缓存依赖于区分 Request Memoization(一个渲染请求)和 Data Cache(跨请求持续存在)的生命周期。

要验证缓存设置,请在部署之前运行 next build 以检查哪些路由编译为静态 (○),哪些路由编译为动态 (λ)。