跳转到内容

预缓存策略:提前把应用外壳送达

发布于

一句话: 预缓存在 service worker 的 install 步骤中,把一组已知、固定的文件存入 缓存,使应用外壳在被需要之前就已在设备上——安装后的首次导航能即时且离线地渲染。它是 运行时缓存的对应物,后者在用户请求时才存储响应。

  • 预缓存 —— 一份在构建时已知的固定清单(HTML 外壳、CSS、JS、图标、字体)。在 install 期间、页面请求之前取回并存储。适合应用外壳及其他关键、始终需要的资源。
  • 运行时缓存 —— 在 fetch 处理器中随请求发生而填充,按选定的缓存策略进行。适合多变、 或太大/太多以致无法预先列出的内容(API 响应、用户图片、文章)。

预缓存与运行时缓存是互补的:预缓存外壳让首次加载快速且具备离线能力,并围绕它运行时缓存 动态内容。

预缓存什么(以及不缓存什么)

Section titled “预缓存什么(以及不缓存什么)”
  • 预缓存: 最小的应用外壳——渲染一个可用首屏所需的 HTML/CSS/JS 与图标。这里列出的一切 都会在 install 事件期间被下载并存储。
  • 改用运行时缓存: 多变、体积大或按需请求的内容,通常用运行时缓存策略处理,而非预缓存。

预缓存骨架;让运行时策略处理其余。

只有当 service worker 能判断某个预缓存文件何时已变更,预缓存才安全。如果你预缓存了 /app.js,之后在同一 URL 部署了新的 /app.js,service worker 需要一个表明内容不同的信号, 否则用户会一直保留陈旧文件。

让变化可被检测的两种方式:

  • 带版本的 URL(/app.abc123.js):内容变化时 URL 本身就会变,因此预缓存条目自然是新 的。Workbox 会把一个已携带版本信息(如内容哈希)的 URL 视为其自身的修订。
  • 每条目一个修订号: 把每个无版本的 URL(如 /index.html)与一个内容修订号配对,这样 即使 URL 相同,当修订号不同时缓存也会重新取回。

这套簿记正是构建期的预缓存清单所自动化的——手工维护极易出错,这也是为何预缓存通常委托 给库来做。

Workbox 的 workbox-precaching 模块消费一份预缓存清单(构建期生成的 { url, revision } 条目 列表),并为你处理整个生命周期:

  • 在 install 期间,取回并存储每个列出的资源。
  • 在 activate 期间,移除先前预缓存中已不在清单里的条目,使旧外壳版本被自动清理。
  • 它从缓存提供预缓存的响应,并在某条目的修订号变化时重新取回。
import { precacheAndRoute } from 'workbox-precaching';
// self.__WB_MANIFEST 在构建期被注入 { url, revision } 条目。
precacheAndRoute(self.__WB_MANIFEST);

Workbox 提供构建工具——workbox-build、workbox-webpack-plugin 与 workbox-cli——来从你 实际的构建产物生成该预缓存清单,使清单与修订号在每次部署时保持同步。

预缓存存放于 Cache Storage 层,独立于浏览器的 HTTP 缓存。两者可能相互影响:一次预缓存取回 本身可能被 HTTP 缓存应答,从而把陈旧副本送进你的预缓存。在这种风险要紧之处,使用带版本的 URL,使每个版本都是不同的请求。留意两层的生命周期,别让它们彼此对抗。

  • 只预缓存最小的应用外壳;动态与大型内容用运行时缓存。
  • 优先使用带版本(哈希)的 URL,让变更的资源成为不同的请求。
  • 对无版本的 URL(如 /index.html),将其与内容修订号配对。
  • 用 Workbox 工具在构建期生成预缓存清单;不要手工维护这份列表。
  • 依赖 Workbox 在 activate 中清理被取代的预缓存。
  • 警惕可能把陈旧资源预缓存进来的 HTTP 缓存交互。