预缓存的注意事项

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

阅读本文档时,请注意这些只是一般性准则。对于您的应用架构和要求,您可能需要执行与此处建议不同的操作,但这些准则可以作为很好的默认设置。

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

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

  • 全局样式表。
  • 提供全局功能的 JavaScript 文件。
  • App 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 浪费带宽。

请勿预缓存 polyfill。依靠运行时缓存,确保它们只缓存到需要缓存的浏览器中,从而避免浪费数据。

总结

使用预缓存时,您需要预先考虑用户实际需要哪些资源,但您肯定能以一种优先考虑未来性能和可靠性的方式,正确行事。

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