前端知库

Compression Dictionary Transport 怎样减少重复下载?

服务端给旧版资源加上 Use-As-Dictionary 响应头,浏览器就把它存成字典;下次请求匹配的新版本时,请求头带上 Available-Dictionary,服务端只需发送以旧版为字典压缩的差量(dcb 或 dcz 编码)。它是实验性功能,目前不是 Baseline,要能退回普通压缩。

参考答案

简答

网站发新版时,app.v2.js 和 app.v1.js 往往只差几行,可用户还是要把整个文件重新下一遍。Compression Dictionary Transport 让浏览器把旧版文件当作“字典”,服务端只用发送相对字典的差量。

流程靠几个头完成。服务端返回 v1 时带上 Use-As-Dictionary: match="/js/app.*.js",浏览器就记住它。以后请求匹配的 v2,请求头会带 Available-Dictionary,值是字典的 SHA-256 哈希;服务端如果有对应字典,就返回 Content-Encoding: dcb(Brotli)或 dcz(Zstandard)的压缩结果。

它适合频繁发版的 JS、CSS、WASM,也可以用 <link rel="compression-dictionary"> 在空闲时预取一份公共字典,给多个页面共用。

要注意,MDN 把它标为实验性、有限可用。字典有效期跟着缓存走,no-store、no-cache 的响应不会成为字典;字典必须和使用它的资源同源,还受缓存分区影响。服务端在请求不带这些头时要照常返回普通压缩。

浏览器和服务端怎样一步步配合

假设第一次访问,服务端返回 app.v1.js,并声明它可以当字典用:

http
Use-As-Dictionary: match="/js/app.*.js", id="dictionary-12345"
Cache-Control: max-age=31536000

match 是一个 URL 模式,写法和 URLPattern 相同,见《URLPattern 适合做路由匹配吗?》。它说明以后哪些地址可以用这份字典。id 是可选的,方便服务端查找字典。

新版本上线后,浏览器请求 app.v2.js。地址匹配上了,所以请求里多了两个头:

http
Accept-Encoding: gzip, br, zstd, dcb, dcz
Available-Dictionary: :pZGm1Av0IEBKARczz7exkNYsZb8LzaMrV7J32a2fFG4=:
Dictionary-ID: "dictionary-12345"

服务端用哈希确认自己手里有同一份字典,就返回以它为字典压缩的结果:

http
Content-Encoding: dcb
Vary: accept-encoding, available-dictionary

为什么要以哈希为准,Dictionary-ID 不够吗?因为 ID 只是帮服务端找文件的提示,MDN 明确说服务端仍须核对 Available-Dictionary 的哈希。哈希对不上就说明浏览器手里的字典不是服务端以为的那份,硬用会解压出错误内容。

Vary 也不能省。同一个地址会因为请求带不带字典返回不同编码,缓存必须按这两个请求头区分,否则别的用户可能拿到一份自己没有字典、无法解压的响应。Vary 的作用见《HTTP 内容协商会怎样影响前端资源和接口响应?》。

为什么有的资源不会被当成字典

字典能存多久,取决于提供它的那个响应是否还新鲜,也就是 Cache-Control。MDN 列出的规则是:no-store 或 no-cache 的响应永远不会成为字典;max-age=0, stale-while-revalidate=N 时字典保留 N 秒;s-maxage 会被忽略,因为浏览器是私有缓存。

所以这里有个常见的坑。HTML 文档通常用 no-cache 或 max-age=0, must-revalidate 来保证每次拿最新版本,可这样它就没法做下一版的字典,浏览器会静默丢弃。Chrome 会在 DevTools 的 Issues 面板提示 freshness 已过期。缓存头的含义见《强缓存与协商缓存如何配合?》。

没有旧版本时怎么用

第一次访问的用户没有旧版文件,可以另外准备一份公共字典,让浏览器空闲时下载:

HTML
<link rel="compression-dictionary" href="/dictionary.dat" />

也可以写在响应头里:Link: </dictionary.dat>; rel="compression-dictionary"。这份字典的响应同样要带 Use-As-Dictionary。字典没有特殊格式,可以是旧版文件、常用字符串集合,或 Brotli 字典生成工具的输出。

如果页面开了 CSP,这个 <link> 的加载受 connect-src 管,没写 connect-src 时看 default-src。

有哪些限制

字典必须与使用它的资源同源。用字典压缩的资源要么与文档同源,要么走 CORS,即带 crossorigin 请求、服务端返回 Access-Control-Allow-Origin。字典还受 HTTP 缓存分区约束,不会跨站共享。MDN 还提到,用户屏蔽 Cookie 或开启额外隐私保护时,浏览器可能关闭这个功能,因为字典可能被用于追踪。

对静态资源,MDN 建议构建时就把差量文件生成好,并决定要为前几个历史版本生成差量。每个 .dcb 文件的结构是固定的魔数头、字典的 SHA-256,然后是压缩数据。

常见追问

它和普通的 Brotli 压缩有什么区别?

普通 Brotli 只能利用文件自身的重复内容。字典压缩还能引用字典里已有的内容,新旧版本重合的部分可以直接引用字典,不必再传一遍。

浏览器不支持会怎样?

不支持的浏览器不会发 dcb、dcz 和 Available-Dictionary,服务端按普通的 Accept-Encoding 返回 gzip 或 br 就行。所以它是渐进增强,服务端要保证两种情况都能处理。

之前有类似的技术吗?

有,叫 SDCH。MDN 提到它在 2017 年被移除,Compression Dictionary Transport 是它的替代方案,标准是 RFC 9842。