高频发布时代的前端交付要重新设计缓存与压缩协同
资源越来越碎、版本越来越勤之后,真正先失控的往往不是压缩率,而是缓存键、字典版本和回源成本一起被发布节奏打乱
前端资源一旦进入高频发布节奏,性能问题很快就不再是“把 Brotli 打开”这么简单。首屏变慢、回源流量抬高、边缘节点 CPU 抖动,表面上像是压缩不够 aggressive,往里看常常是缓存和压缩各自优化,最后在发布链路上互相拆台。
这类问题一般不是在首个版本暴露。最开始,团队只会看到一些分散的信号:某次小改动把静态资源命中率打穿,某个大促前夕边缘压缩 CPU 异常抬高,灰度阶段的回包体积和正式流量对不上。继续查,线索通常会收敛到同一件事:资源内容虽然只改了一点点,但缓存键、chunk 切分和压缩输入已经变成了另一套东西,传输层被迫把整份成本重新吞一遍。
资源哈希稳定之前,压缩收益根本站不住
前端项目走到多页面、多路由和多团队并行发布之后,最容易被忽略的是文件名稳定性。只要 chunk 切分稍微漂移,哪怕业务代码只改了一个按钮文案,最终产物也可能连带改写一串公共 bundle 的 hash。缓存系统看到的是一批全新对象,压缩系统看到的也是一批第一次出现的输入。
这时候再高的压缩率都救不了命中率塌陷。旧文件还躺在边缘节点里,新文件已经换了键;浏览器本地缓存完全失效,CDN 也得重新拉源、重新压缩、重新分发。一次很小的业务变更,就这样被放大成整条传输链路的重复劳动。
真正有用的动作通常不是继续调压缩级别,而是先把发布产物的稳定性收住:
- 公共依赖单独切层,减少业务改动把基础包一起带着换 hash
- 避免把时间戳、构建号这类高频变化直接混进产物内容
- 让同一路由附近的代码尽量落在稳定 chunk,而不是每次编译都被重新洗牌
只有资源身份先稳定下来,缓存才能持续复用,压缩结果才有累计价值。
高频发布会把压缩问题改写成字典版本问题
资源越来越碎之后,单文件 Brotli 或 gzip 仍然重要,但它已经不是全部。真实成本开始转向重复片段:框架运行时代码、样式模板、接口类型声明、打包器生成的包装层,往往在一批版本之间高度相似。发布节奏一快,这些重复片段会被反复传输。
问题在于,压缩字典如果没有跟发布节奏一起管理,很容易从优化项变成扰动项。字典提前切换,旧页面引用新字典会失配;字典切得太碎,边缘节点要维护的版本数量暴涨;字典更新跟资源上线不同步,又会把本来该命中的对象重新打回全量传输。
这也是近期前端交付里一个很实际的变化:缓存策略和压缩协议不能再分别归不同团队各自维护。资源版本、字典版本、缓存键空间,本质上已经是同一个发布问题。
像下面这种分层思路,通常比“全站统一强压缩”更稳:
const policy = {
immutableAssets: 'public, max-age=31536000, immutable',
releaseManifest: 'public, max-age=60, stale-while-revalidate=30',
sharedDictionary: 'versioned-by-release-train'
}
关键不在这三行配置本身,而在它表达出的约束:长生命周期资源、短生命周期清单、压缩字典版本,必须按同一轮发布节奏一起演进。
回源压力往往不是文件太大,而是失效方式太粗
另一个很常见的误判,是把带宽上涨直接归因到页面变重。页面当然在变重,但线上更危险的放大器通常是失效方式。
如果每次发布都按目录、前缀甚至整站去 purge,缓存层就会瞬间失忆。此时哪怕文件体积没有继续增长,回源峰值也会被自己推高。回源一上来,边缘重新压缩、对象重新预热、浏览器重新下载,发布窗口就会从一次小步变更变成全站重新搬运。
这类场景里,最值钱的是可控失效半径:
- 只失效 manifest、HTML 和确实变更的可变资源
- 带 hash 的静态文件尽量不 purge,交给新引用自然切换
- 把发布拆成“先上新资源,再切引用,再回收旧资源”的顺序,而不是一次清空
传输成本真正敏感的,不只是文件大小,还有系统怎样决定哪些内容必须重新取。
适用边界跟资源规模和发布频率一起决定
这套协同设计并不是所有站点都需要。静态页数量不多、资源包也很小、发布频率按周甚至按月走的项目,用传统的 hash 文件名加 Brotli 预压缩,通常已经够稳。
一旦具备下面这些特征,缓存与压缩协同就会迅速变成交付基础设施:
- 每天多次发布,且有灰度、回滚或分区域上线
- 前端产物体积大,公共依赖多,chunk 关系复杂
- CDN、对象存储、边缘压缩和浏览器缓存同时参与传输链路
- 流量高到缓存命中率和回源峰值会直接反映到成本与稳定性
前端交付进入这个阶段后,压缩已经不只是“把文件变小”,缓存也不只是“多存几份内容”。两者一起决定的是:一次很小的业务变更,究竟只是多发一个 chunk,还是要让整条传输链路重新跑一遍。发布越频繁,这个差别越贵。