服务端模板碎片缓存必须按粒度隔离,键需显式包含用户ID、地区码、设备类型等上下文维度,避免污染;失效须主动触发且精准匹配业务事件,禁止全局flush或依赖TTL过期。

服务端模板碎片缓存必须按粒度隔离,不能全局共用一个缓存键
模板碎片(比如 header、sidebar、product-list)如果共用同一套缓存键逻辑,极易导致「缓存污染」:用户A看到用户B的购物车数量,或登录态错乱。根本原因是缓存键没包含足够上下文维度。
常见错误现象:cache.get('header') 返回了未登录版,但当前请求用户已登录;cache.get('product_list') 返回了带促销标签的版本,而当前地区不参与活动。
- 缓存键必须显式拼接关键变量:用户ID、地区码、设备类型、语言、登录态(如
header:uid_123:region_cn:mobile:zh) - 避免用
JSON.stringify(req.query)或req.url直接作键——URL 中可能含 tracking 参数(如utm_source),导致缓存爆炸 - 对纯静态碎片(如 footer),可固定键 + 设置较长 TTL,但需和动态碎片物理隔离(不同 Redis DB 或命名空间)
- PHP 的
apcu_cache或 Node.js 的lru-cache不支持自动过期,必须手动管理 TTL,否则 stale 数据长期滞留
碎片缓存失效必须主动触发,不能依赖被动过期
等 TTL 到期才更新,意味着内容变更后最长要等一整轮缓存生命周期才能生效——对商品价格、库存、促销文案这类高频变更项,不可接受。
使用场景差异极大:CMS 修改文章标题应立刻失效对应 article:123 缓存;用户修改昵称只需失效 profile:uid_123,而非清空整个用户域。
立即学习“前端免费学习笔记(深入)”;
- 写操作路径中必须同步调用
cache.delete('fragment_key'),而不是只删数据库再等缓存自然过期 - 推荐用「标记失效」而非「删除」:设
cache.set('article_123:invalid', true, {ttl: 60}),读取时先查标记,命中则回源并刷新缓存 - 避免用
cache.flushAll()—— 它会连带清掉 header、footer 等稳定碎片,引发雪崩式回源 - 若用 Redis,可用
PUB/SUB实现跨进程失效通知,但需确保所有服务实例都订阅同一 channel
HTML 拼装阶段禁止在缓存碎片里嵌入运行时 JS 变量
服务端缓存的是字符串,不是 DOM。如果碎片 HTML 里写了 <script>window.USER_ID = <?php echo $uid ?></script>,这个 $uid 是缓存生成时刻的值,后续所有请求都会复用它——造成 ID 固化。
典型坑:登录页缓存了带 window.CSRF_TOKEN 的片段,结果所有用户拿到同一个 token,提交表单全失败。
- 所有运行时变量(用户 ID、token、时间戳、随机 nonce)必须在拼装完成后再注入,不能进入缓存体
- 正确做法:缓存纯 HTML 片段(不含 script),拼装后统一用
<script id="runtime-data">{...}</script>注入 JSON - 若必须内联 JS,用占位符如
__USER_ID__,拼装时用str_replace()替换,确保每次都是新鲜值 - Vue/React SSR 场景下,
window.__INITIAL_STATE__同理,绝不能塞进缓存碎片
CDN 或反向代理对碎片 HTML 的缓存必须关闭
CDN(如 Cloudflare)或 Nginx proxy_cache 无法识别「这是模板碎片」,它只看 HTTP 响应头。一旦给碎片接口返回了 Cache-Control: public, max-age=3600,CDN 就会把它当静态资源缓存并分发到边缘节点——导致不同用户看到彼此的个性化内容。
错误配置示例:Nginx 把所有 /api/fragment/* 都 proxy_pass 到 PHP,却统一加了 add_header Cache-Control "public, max-age=3600"。
- 碎片接口必须返回
Cache-Control: no-store或private, max-age=0,明确禁止中间层缓存 - 即使走 AJAX 加载碎片,也要检查 XHR 响应头——浏览器不会因是 JS 请求就绕过缓存规则
- 若用 ESI(Edge Side Includes),CDN 能识别并按需组装,但需服务端输出
<esi:include src="/fragment/header" />,且 CDN 开启 ESI 支持 - 验证方式:curl -I https://yoursite.com/api/fragment/header | grep Cache-Control,确认不是 public 或 long max-age
profile:uid_123,还要删 checkout_step1:uid_123 和 order_preview:uid_123——漏掉任意一个,页面上就会出现不一致的状态。



















