处理导航请求

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

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

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

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

作为一般规则,请勿使用长期有效的 Cache-Control 标头来缓存导航请求的 HTML 响应。它们通常应通过网络(使用 Cache-Control: no-cache)来满足,以确保 HTML 以及后续网络请求链是(合理地)新鲜的。不幸的是,每次用户导航到新页面时都与网络交互意味着每次导航可能会很慢。至少,这意味着它不会始终很快。

不同架构的方案

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

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

小型静态网站

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

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

或者,如果您不想预缓存所有 HTML(可能是因为用户倾向于仅导航到您网站上的一部分网址),则可以使用“陈旧时重新验证”运行时缓存策略。但请注意这种方法,因为每个单独的 HTML 文档都是单独缓存和更新的。如果您的网址数量较少,并且同一组用户频繁访问这些网址,并且您对这些网址彼此独立重新验证感到满意,那么对 HTML 使用运行时缓存是最合适的。

单页应用

单页架构经常被现代 Web 应用程序使用。在单页应用中,客户端 JavaScript 会修改 HTML 以响应用户操作。此模型使用 History API 在用户与 Web 应用交互时修改当前网址,从而导致实际上是“模拟”导航。虽然后续导航可能是“伪造的”,但初始导航是真实的,并且确保初始导航不会被网络阻止仍然很重要。

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

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

多页应用

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

但是对于多页应用的某个子集,您或许可以实现一个 Service Worker,它完全复制 Web 服务器中用于生成 HTML 的逻辑。如果您可以在服务器和 Service Worker 环境之间共享路由和模板信息,并且特别是如果您的 Web 服务器使用 JavaScript(而不依赖于 Node.js 特有的功能,例如文件系统访问),则此方法效果最佳。

如果您的 Web 服务器属于该类别,并且您想探索一种将 HTML 生成从网络移至 Service Worker 的方法,那么 “超越 SPA:PWA 的替代架构”中的指南可以帮助您入门。

其他所有情况

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

Workbox 提供了一个帮助程序库,它可以检测是否支持导航预加载功能,如果支持,则简化了告知您的 Service Worker 使用网络响应的过程。

照片由 Aaron BurdenUnsplash 上拍摄