模板碎片缓存必须区分静态与动态边界:通用页脚、产品卡片结构等不依赖请求上下文的内容可缓存;含{{.currentuser.name}}等动态字段的模板不可整块缓存,须按locale、设备、权限等维度切片key。

模板碎片缓存必须区分静态与动态边界
服务端渲染中,把整个 HTML 当作一个块来缓存,等于在用户态数据上埋雷。只要模板里出现 {{.CurrentUser.Name}} 或 {{.CSRFToken}},哪怕只占一行,整块就不能进缓存——否则 A 用户的登录态会污染 B 用户的页面。
真正可缓存的是:通用页脚、产品卡片结构(仅含 product.Name 和 product.Price)、帮助文档段落(纯 Markdown 渲染后无 session 数据)。这些内容不依赖请求上下文,变更频率低,复用广。
- 错误做法:
cache.Get("header")→ 所有用户拿到同一份 HTML - 正确做法:
cache.Get("header:zh-CN:mobile:guest"),缓存键必须包含 locale、设备类型、权限等级、AB 分组等维度 - 构建缓存键时,别漏掉
req.Header.Get("User-Agent")的指纹特征,移动端和桌面端的<picture></picture>候选集不同,混用会导致图片加载失败
多级嵌套模板的 hydration ID 冲突怎么破
微服务聚合场景下,订单、用户、推荐三个服务各自返回带 id="loading" 的碎片,合并后 DOM 中只剩一个生效,JS 事件绑定错位、焦点丢失、document.getElementById("loading") 总是返回第一个节点。
解决方案不是删 ID,而是动态注入前缀:
- 服务端渲染时,给每个碎片加唯一前缀:
id="{{.FragmentID}}-loading" - 客户端 hydrate 前,执行
document.querySelectorAll('[id^="{{.FragmentID}}-"]')批量重写 ID,再挂逻辑 - 绝对不要在模板里写固定
id="sidebar"—— 看似不会重复,实则在聚合渲染时必然冲突
这个细节在 SSR + 微前端架构中尤其致命,且无法靠前端调试发现,只有真实用户反馈“按钮点不动”“弹窗打不开”才暴露。
HTML <template></template> 标签在 SSR 中的缓存误解
很多人把 SSR 输出的 HTML 字符串塞进 <template id="list-item"></template> 再注入 DOM,以为能复用浏览器缓存机制——这是错的。<template></template> 是客户端运行时惰性容器,服务端根本不会解析它,也不参与 SSR 缓存链路。
真正起作用的是服务端模板引擎自身的缓存层(如 Marko 的 compiler cache、Thymeleaf 的 fragment cache),<template></template> 标签本身不改变服务端缓存行为。
- SSR 输出含
<template id="list-item"></template>的 HTML?没问题,但它的缓存由服务端控制 - 若服务端已缓存完整 HTML,再用 JS 抽出
document.getElementById('list-item').content,只是从已缓存字符串里取一段,不是二次缓存 - 想靠
<template></template>实现客户端模板复用?可以,但和 SSR 缓存无关,也解决不了服务端数据污染问题
动态字段明文渲染是最大质量漏洞
只要 user_id=123456 出现在初始 HTML 的 <div data-id="123456"> 里,或 <code><script>var price = 299.00</script> 中,就等于白送给爬虫。这不是“可能被爬”,而是“必然被爬”。
安全底线只有一条:不在初始 HTML 渲染敏感字段。哪怕只是展示,也得走 fetch 动态拉取,且接口必须校验登录态 + 设备指纹 + 请求频率。
- CSS 伪元素拼接价格(
::before { content: "¥" "2" "9" "9"; })只防正则提取,Puppeteer 可直接读window.getComputedStyle(el, '::before').content - Unicode 同形字混淆(用西里尔 а 替代拉丁 a)影响屏幕阅读器和复制粘贴,禁用于表单 placeholder 或提交值
-
<!-- order_no: ABC789 -->注释更危险——静态扫描工具一扫就出,比明文还显眼
HTML 结构层面的治理,本质是守住“初始 HTML 不含敏感态”这条红线。其余所有混淆、拆分、缓存策略,都只是在这条线之上做 ROI 权衡,而非替代它。











