处理导航请求

使用 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,您可以指定要用作 App Shell 的 HTML 文档,以及可选的允许和拒绝列表,从而将此行为限制为网址子集。

多页应用

如果您的网络服务器以动态方式生成您网站的 HTML,或者如果您有数十个以上不同的网页,则在处理导航请求时要避开网络。其他中的建议可能适用于您。

但是,对于多页应用的某一子集,您可以实现一个 Service Worker,它完全复制网络服务器中用于生成 HTML 的逻辑。如果您可以在服务器和 Service Worker 环境之间共享路由和模板信息,则效果最佳,尤其是当您的网络服务器使用 JavaScript(不依赖于文件系统访问等特定于 Node.js 的功能)时。

如果您的网络服务器属于该类别,并且您想要探索一种方法来将 HTML 生成从网络移到 Service Worker,则可以从超越 SPA:PWA 的备用架构中的指南开始。

其他

如果您无法使用缓存的 HTML 响应导航请求,则必须采取措施来确保向网站添加 Service Worker(以处理其他非 HTML 请求)不会拖慢导航速度。在不使用 Service Worker 来响应导航请求的情况下启动 Service Worker 会产生些许延迟(如使用 Service Worker 构建更快、更弹性的应用中所述)。为了减少此开销,您可以启用一项称为导航预加载的功能,然后使用预加载到 fetch 事件处理脚本中的网络响应

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

照片由 Aaron Burden 拍摄于 Unsplash 网站