预缓存的注意事项

本文档之前介绍了预缓存,但对正确做法还不够深入。这一点非常重要,因为无论您是否使用 Workbox,都很容易预缓存太多内容,并可能会浪费数据和带宽。您应注意预缓存载荷对用户体验的影响。

在阅读本文档时,请注意,这些只是一般准则。根据您的应用架构和要求,您可能需要采取与本文建议不同的做法,但这些指南可作为很好的默认设置。

正确做法:预缓存关键静态资产

最佳的预缓存候选对象是关键静态资源,但什么算作“关键”资源呢?从开发者的角度来看,可能很想将整个应用视为“关键应用”,但用户的角度才是最重要的。将关键资源视为提供用户体验完全必需的资源:

  • 全局样式表。
  • 提供全局功能的 JavaScript 文件。
  • 应用 Shell HTML(如果适用于您的架构)。

温馨提示:这些只是一般指南,并非硬性建议。预缓存资产时,最好少预缓存,而不是多预缓存。

正确做法:为多页网站预缓存离线后备广告

对于典型的多页网站,您可能依靠网络优先仅限网络缓存策略来处理导航请求。

在这种情况下,您需要确保 Service Worker 预缓存,并在用户在离线状态下发出导航请求时使用离线后备页面进行响应。在 Workbox 中实现此目的的一种方法是,结合使用网络专用策略和离线回退,同时利用导航预加载

import {PrecacheFallbackPlugin, precacheAndRoute} from 'workbox-precaching';
import {registerRoute, Route} from 'workbox-routing';
import {NetworkOnly} from 'workbox-strategies';
import * as navigationPreload from 'workbox-navigation-preload';

navigationPreload.enable();

// Ensure that /offline.html is part of your precache manifest!
precacheAndRoute(self.__WB_MANIFEST);

// The network-only callback should match navigation requests, and
// the handler for the route should use the network-only strategy, but
// fall back to a precached offline page in case the user is offline.
const networkOnlyNavigationRoute = new Route(({request}) => {
  return request.mode === 'navigate';
}, new NetworkOnly({
  plugins: [
    new PrecacheFallbackPlugin({
      fallbackURL: '/offline.html'
    })
  ]
}));

registerRoute(networkOnlyNavigationRoute);

这样可以确保在用户离线时前往不在缓存中的页面,至少会获得一些离线内容。

可能适用:考虑进行推测性预缓存

这是一个很大的“可能”,但预缓存仅在特定情况下使用的素材资源会带来潜在好处。可以这样理解:用户会产生一些额外的前期数据下载,这样会加快未来对这些资产的请求的速度,这属于推测性收益。

不过,有一点要提醒你:如果你决定这样做,请非常小心。这样做很容易浪费数据,应该以数据为依据做出决策。此外,请避免推测性地预缓存频繁更改的资源,因为每次预缓存代码检测到新的修订版本时,用户都会消耗额外的数据流量。观察数据分析中的用户流,了解用户倾向于去哪里。如果您对推测性预缓存资产存有疑问,则可能最好不要这么做。

不可行:预缓存静态 HTML

本指南更适用于静态网站,其中独立的 HTML 文件是由静态网站生成器生成或手动创建,而不是由应用后端动态生成或提供。如果您的架构符合此要求,那么最好不要预缓存网站的每个 HTML 文件。

预缓存整个网站的 HTML 文件有一个问题,那就是现在预缓存的标记始终会在稍后从缓存中提供,直到 Service Worker 更新。这种做法对于性能非常好,但如果您网站的 HTML 经常更改,则可能会导致缓存大量流失。

不过,这条规则也有一些例外情况。如果您要部署包含几个静态 HTML 文件的小型网站,最好预先缓存所有这些网页,以供离线使用。如果您的网站特别大,不妨考虑推测性地预缓存一些高价值网页和一个离线后备广告,并依靠运行时缓存为您缓存其他网页。

错误做法:预缓存自适应图片或网站图标

这并不是一项一般的准则,而是一条规则。自适应图片是一种用于解决复杂问题的复杂解决方案:您的许多用户使用多种设备,每种设备的屏幕尺寸、像素密度和对替代格式的支持情况各不相同。如果您预缓存了整组自适应图片,那么当用户最终只下载其中一张图片时,您可能会预缓存多张图片。

网站图标的情况与此类似,因为网站通常会针对不同场景部署一整套网站图标。大多数情况下,系统只会请求一个网站图标,因此预缓存整个网站图标集也同样会造成浪费。

对用户有所帮助,不要预缓存自适应图片和网站图标集。改为依赖运行时缓存。如果您必须预缓存图片,请预缓存不属于一组自适应图片或网站图标的广泛使用的图片。SVG 在流量消耗方面的风险较低,无论指定屏幕的像素密度如何,单个 SVG 都能以最佳方式呈现。

错误做法:预缓存 polyfill

为 API 提供不同的浏览器支持是 Web 开发者持续面临的挑战,而 polyfill 是克服该挑战的一种方式。为尽可能降低 polyfill 的性能开销,一种方法是进行功能检查,并仅针对需要它们的浏览器加载 polyfill。

由于有条件地加载 polyfill 是在运行时相对于当前环境发生的,因此预缓存 Polyfill 就像是在赌博。有些用户将从中受益,而另一些用户则会浪费带宽来购买不必要的 polyfill。

不要预缓存 Polyfill。依靠运行时缓存,确保它们仅在需要缓存的浏览器中缓存,从而避免浪费数据。

总结

预缓存需要提前一些考虑用户实际需要哪些资源,但如果您重视未来性能和可靠性,您绝对可以做到这一点。

如果您不确定是否应预缓存某些资源,最好的办法可能是告知 Workbox 排除这些资源,并创建运行时缓存路由来处理它们。无论采用哪种方式,本文档稍后都会详细介绍预缓存,以便您日后能够将这些原则应用于您的预缓存逻辑。