处理导航请求

使用 Service Worker 响应导航请求,而无需等待网络。

导航请求是指每当您在导航栏中输入新网址或点击网页上的链接转到新网址时,浏览器发出的 HTML 文档请求。这就是 Service Worker 对性能影响最大的地方:如果您使用 Service Worker 响应导航请求,而无需等待网络,则可以确保导航速度可靠,并且在网络不可用时具有弹性。与 HTTP 缓存相比,这是 Service Worker 带来的最大性能优势。

如识别从网络加载的资源指南中所述,导航请求是网络流量“瀑布流”中可能发出的众多请求中的第一个。您通过导航请求加载的 HTML 会启动对图片、脚本和样式等子资源的所有其他请求的流程。

在 Service Worker 的 fetch 事件处理脚本中,您可以通过检查 FetchEvent 上的 request.mode 属性来确定请求是否为导航。如果设置为 'navigate',则为导航请求。

一般而言,请勿使用长期有效的 Cache-Control headers 来缓存导航请求的 HTML 响应。通常,这些请求应通过网络(使用 Cache-Control: no-cache)来满足,以确保 HTML 以及后续网络请求链(合理地)保持最新。遗憾的是,每次用户导航到新页面时都访问网络意味着每次导航可能都很慢。至少,这意味着它不会可靠地快速。

不同的架构方法

弄清楚如何在避免网络请求的同时响应导航请求可能很棘手。正确的方法在很大程度上取决于您网站的架构以及用户可能访问的唯一网址的数量。

虽然没有一种万能的解决方案,但以下一般准则应该可以帮助您确定哪种方法最可行。

小型静态网站

如果您的 Web 应用包含相对较少(例如几十个)的唯一网址,并且每个网址都对应一个不同的静态 HTML 文件,那么一种可行的方法是缓存所有这些 HTML 文件,并使用适当的缓存 HTML 响应导航请求。

使用预缓存,您可以在安装 Service Worker 后立即提前缓存 HTML,并在每次重新构建网站和重新部署 Service Worker 时更新缓存的 HTML。

或者,如果您不想预缓存所有 HTML(可能是因为用户往往只会访问您网站上的部分网址),则可以使用 stale-while-revalidate 运行时缓存策略。不过,请谨慎使用此方法,因为每个单独的 HTML 文档都是单独缓存和更新的。如果您有少量网址会被同一组用户频繁访问,并且您愿意让这些网址彼此独立地重新验证,那么对 HTML 使用运行时缓存是最合适的。

单页应用

现代 Web 应用经常使用单页架构。其中,客户端 JavaScript 会根据用户操作修改 HTML。此模型使用 History API 在用户与 Web 应用互动时修改当前网址,从而实现有效的“模拟”导航。虽然后续导航可能是“虚假”的,但初始导航是真实的,因此确保其不会在网络上被屏蔽仍然很重要。

幸运的是,如果您使用的是单页架构,则可以遵循一种简单的模式,从缓存中提供初始导航:应用 shell。在此模型中,无论请求的网址是什么,Service Worker 都会通过返回已预缓存的同一单个 HTML 文件来响应导航请求。此 HTML 应为基本内容,可能包含通用加载指示器或骨架内容。浏览器从缓存中加载此 HTML 后,您现有的客户端 JavaScript 会接管,并为原始导航请求中的网址呈现正确的 HTML 内容。

Workbox 提供了实现此方法所需的工具;navigateFallback option 允许您指定要用作应用 shell 的 HTML 文档,以及可选的许可名单和拒绝名单,以将此行为限制为网址的子集。

多页应用

如果您的 Web 服务器动态生成网站的 HTML,或者您有几十个以上的唯一网页,那么在处理导航请求时,避免网络请求会困难得多。其他所有情况中的建议可能适用于您。

但对于某些多页应用,您或许可以实现一个 Service Worker,它能完全复制 Web 服务器中用于生成 HTML 的逻辑。如果您可以在服务器和 Service Worker 环境之间共享路由和模板信息,尤其是当您的 Web 服务器使用 JavaScript(不依赖于 Node.js 专属功能,例如文件系统访问权限)时,这种方法的效果最佳。

如果您的 Web 服务器属于该类别,并且您想探索一种将 HTML 生成从网络移至 Service Worker 的方法,请参阅超越单页应用:PWA 的替代架构中的指南,开始着手。

其他

如果您无法使用缓存的 HTML 响应导航请求,则必须采取措施,确保向网站添加 Service Worker(用于处理其他非 HTML 请求)不会最终减慢导航速度。启动 Service Worker 而不使用它来响应导航请求会引入少量延迟(如使用 Service Worker 构建更快、更具弹性的应用中所述)。您可以通过启用名为导航预加载的功能,然后使用已在 fetch 事件处理脚本中预加载的网络响应,来缓解这种开销。

Workbox 提供了一个辅助库,用于检测是否支持导航预加载,如果支持,则简化了告知 Service Worker 使用网络响应的过程。