先记住这个答案
在 Vercel 等平台,Next.js 构建时会将 SSG 生成的静态 HTML 等资产推送到边缘节点缓存,用户请求直接命中。运行时动态请求(如 SSR、API Routes 中的 fetch)的数据获取结果受 Next.js 缓存机制(如 fetch 的 cache 选项)影响,存储在 Vercel Data Cache 中。但启用 ISR(配置 revalidate)的页面会在首次动态访问时生成完整页面并写入 Full Route Cache,使该页面也进入边缘缓存;随后按 revalidate 间隔重验证并更新边缘。因此构建与运行并非完全隔离,而是由 revalidate 衔接。Data Cache 只缓存数据,边缘缓存既服务静态产物,也能缓存 ISR 生成的动态页面。
- 构建时静态资产进边缘缓存
- 运行时动态数据走请求级缓存
- ISR 页面通过 revalidate 进入并更新边缘缓存
分层缓存机制:构建产物与运行数据如何分流
在Vercel部署时,Next.js的构建阶段会执行generateStaticParams和静态渲染,生成纯静态的HTML和RSC payload文件。这些文件被上传至CDN边缘节点,用户请求默认路径时直接返回,无需访问函数。这类资产包括SSG页面、静态图片、预构建的JSON数据等。它们通过Cache-Control头(通常s-maxage=31536000, stale-while-revalidate)在边缘缓存中持久存在。
运行时则处理动态请求,如SSR、ISR的按需重新验证、Server Actions等。这些请求需要执行函数,数据获取逻辑中使用fetch的cache选项(如force-no-store)或revalidate配置。Vercel提供Data Cache来存储跨请求的数据,但它独立于边缘CDN缓存。运行时的缓存键包括URL参数和请求头等,但边缘缓存只基于URL和accept等标准头判断,两者并不共享键空间。
产品详情页的构建与运行时缓存对比
假设一个电商站点有/products/[id]路由,使用generateStaticParams构建前1000个热门产品的页面。构建时,这些页面作为静态资产进入边缘缓存,用户访问/products/123时,Vercel边缘节点直接响应,不触发函数调用。这是构建时缓存的效果,可承受极高并发。
若新增一个非预生成的产品ID为1001且路由启用了revalidate = 60,请求首次触发动态生成:函数执行数据获取,若Data Cache未过期则快速返回,否则回源;渲染出的 HTML 会经 ISR 写入边缘 Full Route Cache。之后在 60 秒内的到期请求直接命中边缘缓存,无需再次执行函数;60 秒后首个请求重新执行并更新缓存。
缓存分层失效与覆盖条件
当产品ID为123的数据更新时,需要同步协调边缘缓存与 Data Cache。对于构建时未配置 revalidate 的纯静态页面,要主动调用 revalidatePath/revalidateTag 消除边缘缓存;对于配置了 revalidate 的 ISR 页面,到期后的首个请求会自动重新生成,若需立即更新仍可主动调用失效函数强制刷新。Data Cache 中的数据按自身 revalidate 过期,但页面重新渲染时若数据缓存命中会直接使用旧值,因此清除边缘缓存时也要注意数据缓存状态。
另一种情况是动态请求路径全部走运行时,边缘缓存不参与,因此无法享受CDN的延迟削减。例如使用cookies()读取用户身份触发动态渲染,整个页面变成动态的,构建时的静态化不生效,必须每次请求执行函数。代价是性能依赖函数冷启动和Data Cache命中,但可以保证个性化数据。
容易答错的地方
- 认为所有数据都进边缘缓存
- 只有构建时静态资产和 ISR 按需生成的页面才会进入边缘缓存。运行时通过
fetch获取的动态数据存在 Vercel Data Cache 中,不直接暴露到边缘,但 ISR 页面生成时会将渲染结果写入边缘 Full Route Cache。 - 混淆`revalidate`与边缘缓存失效
revalidate控制 ISR 页面自动重新生成的间隔,到期后的下一次请求会触发重新渲染并更新边缘缓存。revalidatePath/revalidateTag用于在不等待自然过期时主动强制失效。两者是互补的控制方式,而不是互斥选择。
面试官还会怎么问?
如何确认某个页面是静态还是动态?
在 next build 输出中,○ 表示静态渲染(含 ISR),ƒ 表示动态渲染;App Router 和 Pages Router 使用相同的符号。静态和 ISR 页面生成后都会进入边缘缓存,动态页面默认不缓存,除非通过 ISR 或显式缓存参与。
使用`fetch`的`cache: 'force-cache'`会进边缘缓存吗?
不会,force-cache只指示Next.js复用Data Cache中的数据,不改变页面本身的静态性。只有页面启用静态生成(无动态API)时才可能将整个页面输出到边缘缓存。
Vercel上的动态页面能使用边缘缓存吗?
可以,但需要显式设置Cache-Control头并处理动态数据。例如在 Route Handler 中设置响应头:return new Response(html, { headers: { 'Cache-Control': 's-maxage=60' } }),但仅适合无个性化且纯 GET 请求的场景。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。