尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
Next.js App Router 的缓存层次:从 fetch 缓存到完整路由缓存的四层模型
Next.js App Router 的缓存层次从 fetch 缓存到完整路由缓存的四层模型Next.js App Router 的缓存机制是影响应用性能和数据新鲜度的核心设计。理解其四层缓存模型——请求记忆化、数据缓存、完整路由缓存、路由器缓存——有助于避免数据过期、缓存穿透和构建体积膨胀等问题。一、四层缓存的架构总览Next.js 的缓存从底层的数据获取到顶层的客户端路由缓存形成了一个逐层递进的体系。每一层的失效和更新策略相互独立但上层缓存的有效性依赖下层缓存的新鲜度graph TB subgraph 客户端 A[路由器缓存br/Router Cache] end subgraph 服务端 B[完整路由缓存br/Full Route Cache] C[数据缓存br/Data Cache] D[请求记忆化br/Request Memoization] end E[外部 API / 数据库] D --|缓存 fetch 结果| E C --|缓存序列化数据| D B --|缓存 RSC Payloadbr/和 HTML| C A --|缓存 RSC Payloadbr/在客户端内存| B B -.-|revalidatePathbr/revalidateTag| C C -.-|revalidate 选项| D四层缓存按作用范围分为请求级、页面级和应用级。请求记忆化在同一渲染 Pass 内生效数据缓存在多个请求间共享完整路由缓存作用于构建或请求时生成的静态页面路由器缓存驻留在客户端浏览器内存。二、请求记忆化同一渲染 Pass 内的去重请求记忆化是 React 的cache()函数和fetch的自动去重机制共同实现的。在同一个页面渲染过程中多次相同的fetch请求会被自动合并import { cache } from react; /** * 数据库查询函数使用 React cache() 实现请求级去重 * 同一渲染 Pass 内多次调用只执行一次 */ const getProductById cache(async (id: string) { console.log([DB Query] 查询产品: ${id}); // 模拟数据库查询延迟 await new Promise((resolve) setTimeout(resolve, 100)); return { id, name: 产品 ${id}, price: Math.floor(Math.random() * 1000), }; }); /** * 产品详情页面多个组件独立调用 getProductById * 但在同一请求中只查询一次数据库 */ async function ProductPage({ params, }: { params: { id: string }; }) { // 即使 ProductTitle 和 ProductPrice 各自调用, // 同一渲染 Pass 内只执行一次数据库查询 return ( main ProductTitle productId{params.id} / ProductPrice productId{params.id} / ProductReviews productId{params.id} / /main ); } async function ProductTitle({ productId }: { productId: string }) { const product await getProductById(productId); return h1{product.name}/h1; } async function ProductPrice({ productId }: { productId: string }) { const product await getProductById(productId); return span¥{product.price}/span; }关键点在于请求记忆化只在同一渲染 Pass内生效。不同 HTTP 请求之间的渲染是独立的不会共享cache()的结果。如果需要在多个请求间共享数据需要使用下一层——数据缓存。三、数据缓存跨请求的数据持久化数据缓存是 Next.js 持久化fetch结果的内置机制。它的行为由cache和next.revalidate选项控制/** * 数据缓存的四种典型使用模式 */ // 模式 1: 强制缓存默认行为 // 构建时 fetch,结果永久缓存,除非手动失效 async function getStaticData() { const res await fetch(https://api.example.com/static-data, { cache: force-cache, // 默认值,可省略 }); return res.json(); } // 模式 2: 不缓存 // 每次请求都重新 fetch async function getDynamicData() { const res await fetch(https://api.example.com/dynamic-data, { cache: no-store, }); return res.json(); } // 模式 3: 定时重新验证ISR 模式 // 缓存结果,但每隔指定秒数后重新获取 async function getRevalidatedData() { const res await fetch(https://api.example.com/updates, { next: { revalidate: 60 }, // 每 60 秒重新验证 }); return res.json(); } // 模式 4: 按标签重新验证 // 通过 revalidateTag 精确控制缓存失效 async function getTaggedData(category: string) { const res await fetch( https://api.example.com/posts?category${category}, { next: { tags: [posts-${category}, all-posts], }, } ); return res.json(); } // 在 Server Action 或 Route Handler 中手动触发重新验证 import { revalidateTag, revalidatePath } from next/cache; /** * 发布新文章后的缓存刷新逻辑 */ export async function publishPost(formData: FormData) { use server; const category formData.get(category) as string; // 保存文章到数据库 await savePost(formData); // 精确失效只刷新相关分类的文章列表缓存 revalidateTag(posts-${category}); revalidateTag(all-posts); // 同时刷新页面路径的完整路由缓存 revalidatePath(/categories/${category}); revalidatePath(/); }数据缓存的实际挑战在于缓存粒度的选择。revalidateTag提供了比revalidatePath更精确的控制但需要在代码中建立清晰的标签命名约定。失控的标签会导致应该刷新的没刷新不应该刷新的反复刷。四、完整路由缓存与路由器缓存完整路由缓存在服务端存储渲染后的 RSC Payload 和 HTML。静态路由在构建时生成并永久缓存动态路由根据数据缓存的 revalidate 周期决定何时重新渲染。import { unstable_cache } from next/cache; /** * 使用 unstable_cache 手动控制路由缓存的片段 * 将数据获取和渲染结果分离缓存 */ const getCachedPageData unstable_cache( async (userId: string) { // 聚合多个数据源 const [profile, posts, followers] await Promise.all([ fetch(https://api.example.com/users/${userId}).then( (r) r.json() ), fetch( https://api.example.com/users/${userId}/posts ).then((r) r.json()), fetch( https://api.example.com/users/${userId}/followers ).then((r) r.json()), ]); return { profile, posts, followers }; }, [user-profile-page], // 缓存标签 { revalidate: 300, // 5 分钟后重新验证 tags: [user-profile], } ); /** * 用户主页组合缓存数据和实时数据 */ async function UserProfilePage({ params, }: { params: { userId: string }; }) { // 使用缓存数据渲染主要区域 const cachedData await getCachedPageData(params.userId); // 某些区域使用 no-store 获取实时数据 const onlineStatus await fetch( https://api.example.com/users/${params.userId}/online, { cache: no-store } ).then((r) r.json()); return ( div ProfileHeader data{cachedData.profile} / OnlineBadge status{onlineStatus} / PostList posts{cachedData.posts} / FollowerList followers{cachedData.followers} / /div ); }路由器缓存位于客户端在浏览器的内存中缓存已访问页面的 RSC Payload。它的独特之处在于有两个有效期静态页面缓存 5 分钟动态页面缓存 30 秒。在页面间导航时路由器缓存提供了即时的渲染结果避免了服务端重新获取。// 这四个操作会触发客户端路由器缓存失效 // 在 Server Action 中调用 use server; import { revalidatePath } from next/cache; import { redirect } from next/navigation; export async function updateProfile() { // 1. revalidatePath 同时失效服务端和客户端缓存 revalidatePath(/profile); // 2. redirect 会清除当前路由的客户端缓存 redirect(/profile); // 3. router.refresh() 在客户端调用,重新获取当前路由 // 4. Cookie 的 set/delete 操作会触发完整页面刷新 }路由器缓存的一个常见问题是用户在页面 A 执行了数据变更操作导航到页面 B 再返回页面 A 时看到的是缓存中的旧数据。解决方案是在变更操作后调用router.refresh()或在 Server Action 中使用revalidatePath。五、总结Next.js App Router 的四层缓存模型构成了一个完整的缓存谱系。请求记忆化解决同一渲染 Pass 内的重复请求问题对开发者完全透明。数据缓存通过cache选项和next.revalidate控制跨请求的数据持久化。完整路由缓存将渲染结果序列化存储决定了页面的静态/动态特性。路由器缓存在客户端提供即时导航体验但细粒度的失效策略是正确使用它的前提。在使用这些缓存时需要关注一个基本原则缓存的粒度应该与数据的变更频率匹配。高频率变更的数据使用no-store或短revalidate变更频率低的数据使用长revalidate或force-cache通过revalidateTag在数据变更时精确失效而不是依赖定时重新验证。错误的缓存策略会导致数据不一致而正确的分层缓存能够将页面加载时间从秒级降低到毫秒级。
RELATED

相关推荐

K8s+Ray:大模型时代AI Workload调度的通用范式

K8s+Ray:大模型时代AI Workload调度的通用范式

随着大模型时代的到来,AI 基础设施(AI Infra)正在经历深刻的变革。面对日益复杂的计算需求,传统上与单一计算范式深度耦合的调度系统已难以应对全局性挑战。本文结合开源社区的演进趋势与工业界的超大规模落地实践,深入…

📅 2026/10/24 11:23:07
2026东莞小程序定制开发的企业综合实力盘点

2026东莞小程序定制开发的企业综合实力盘点

2025-2026年国内小程序开发服务市场规模达到826亿元,同比增长32.7%,其中定制化小程序服务占比提升至68%。 东莞作为粤港澳大湾区制造业重镇,2025年企业开发小程序的需求量同比激增67%,制造业定制化需求占比达52%。面对众多服务商…

📅 2026/10/8 13:21:39
洛雪音乐音源终极指南:如何快速获取全网最新音乐资源

洛雪音乐音源终极指南:如何快速获取全网最新音乐资源

洛雪音乐音源终极指南:如何快速获取全网最新音乐资源 【免费下载链接】lxmusic- lxmusic(洛雪音乐)全网最新最全音源 项目地址: https://gitcode.com/gh_mirrors/lx/lxmusic- 你是否正在为洛雪音乐播放器寻找高质量的音乐资源?想要免费获得全网最…

📅 2026/10/25 16:24:12
MORE NEWS

更多资讯

📰

线上英语教学工具怎么选?2026最新避坑清单

【摘要】线上英语教学工具越来越多,选错了不仅浪费预算,还会打乱教学节奏。这篇文章从一线教学场景出发,拆解工具选型的几个核心维度,包括课前备课、课中互动、课后批改和数据反馈,同时整理了一份实操层面的避坑清单&a…

📰

英语薄弱知识点,2026年最新培优课怎么选?

摘要:英语培优选课的关键不在功能多,而在诊断准。 本文从英语薄弱知识点的真实成因切入,拆解2026年培优课选型的三个硬指标:诊断颗粒度、训练闭环完整度、内容本地化匹配度,并结合天学网英语培优体系的技术逻辑做客观分…

📰

英语写作批改工具,最新5个避坑挑选要点

上学期期末,我帮一个英语教研组看他们新采购的批改系统。打开后台那一刻我愣住了——三万多字的批改记录里,超过六成标注都是“主谓一致”“拼写错误”这类表层问题。老师们花了钱、花了培训时间,最后拿到手的还是一份高级版语法检查器。这事…

📰

拍拍贷风控数据集7z解压与特征工程实战指南

简介:这份资源是拍拍贷风控大赛的实战数据集,面向金融风控方向的学习者、数据挖掘竞赛选手及信用评分建模从业者,用于练习用户违约预测与信用评估全流程。压缩包共176个文件,约37.37MB,以csv数据表为主,涵盖…

📰

AZ-900必过考试真题怎么刷?从背题到理解的备考方法全解析

简介:一份面向微软AZ-900认证考生的考试真题合集,内容以docx文档形式整理,收录了具有代表性的模拟试题及详细解析。资料逐题剖析PaaS平台不提供操作系统直接访问、但支持自动扩展等关键特性,并解释云成本从资本性支出(…

📰

AI Agent时代的Shell:重新定义命令行交互与自动化

1. 为什么一个用了十几年的老工具,突然想聊“下一代”在终端里泡了十几年,我一直觉得 Shell 是一个“用起来很痛、但离不开”的东西。最近两个月,我密集使用了几款 AI 编程工具和命令行代理,一个念头越来越强烈:Shell …

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬