缓存策略设计
给一个页面定缓存方案的决策流程:数据归属 → 新鲜度要求 → 变更触发方式,落到 cacheLife / cacheTag / revalidateTag / 默认动态
决策流程:三问收敛到一行配置
先定性,再定量;跳过第一问直接讨论 revalidate 多少秒,是缓存事故的主要来源
给一个页面定缓存方案,依次回答三问:
① 数据归属:内容对所有访客一样吗?
│
├── 不一样(会话/个人数据)
│ → 不进共享缓存:保持默认动态,cookies()/headers() 取会话
│ 或干脆下沉为客户端取数(SWR,见 /data/client-fetching)
│
└── 一样(公共内容)
│
② 新鲜度要求:数据过期多久算事故?
│
├── 一秒都不行(行情、库存、计数器)
│ → 什么都不写:cacheComponents 下取数默认动态,每请求实时渲染;
│ 需整页等请求到达时用 connection() 显式声明
│
├── 分钟~小时级可接受(榜单、资讯流)
│ → "use cache" + cacheLife({ revalidate: <秒数> })
│ (时间驱动再生,对齐旧 export const revalidate)
│
└── 几乎不变,但变了要立即生效(文档、商品详情)
│
③ 变更触发方式:谁通知缓存失效?
├── 定时轮询就够 → "use cache" + 长窗口 cacheLife(如 86400)
└── 内容系统/后台有 webhook → "use cache" + cacheTag,
webhook 里 revalidateTag / revalidatePath 按需失效,
平时永久静态(见 /rendering/ppr)三个场景卡:照流程走一遍
同样的问题清单,不同的答案组合出完全不同的配置
营销页 / 落地页
- ① 归属:所有人一样 ✓
- ② 新鲜度:小时级可接受
- ③ 触发:发布频率低,定时即可
"use cache" + cacheLife(3600)
长窗口缓存(旧模型的长 ISR):构建后整页静态进 CDN,过期后后台再生; 流量越大越划算,首字节成本趋近于零
仪表盘 / 监控页
- ① 归属:按登录人不同 ✗
- ② 新鲜度:秒级
- ③ 触发:持续变化,无所谓失效
默认动态(必要处 connection())
首屏出壳与基础数据,高频指标下沉客户端轮询 (SWR refreshInterval);共享缓存在这里只制造脏数据
内容站 / 商品详情
- ① 归属:所有人一样 ✓
- ② 新鲜度:平时无所谓,改稿须立即生效
- ③ 触发:CMS 有发布 webhook
"use cache" + cacheTag → revalidateTag("post-123")
平时永久静态(缓存不设时限);CMS webhook 调 Route Handler, 按 tag 精准失效 —— 既有静态的成本,又有按需的新鲜度
决策要点:什么时候别上缓存方案
缓存是默认项之外的优化,不是每个页面都配拥有策略
- 拿不准就先动态:cacheComponents 下「不声明缓存」就是安全的默认值, 缓存策略要在想清楚「脏了谁受影响」之后再用 "use cache" 引入;先缓存后排查, 比先动态后优化痛苦得多
- 低流量页面别精心调参:一天几十次访问的后台页, ISR/tag 体系带来的运维复杂度超过省下的渲染成本
- 别让标签体系失控:revalidateTag 的威力来自 精准的标签设计;标签命名没有规范(谁打、打几个、什么粒度)时, 按需失效很快退化成 revalidatePath("/") 式的全站轰炸
- 机制底座(四层缓存各自的失效方式与排查顺序)见四层缓存对照台;本页是它的「选型上游」