一般情况下,当请求一个包含 Service Worker 的页面,并且此 Service Worker 尚未运行,那么浏览器将会等到 Service Worker 启动之后才会发起导航请求(如下图),也由于受各种因素的影响,Service Worker 的启动时间会有不同程度的延迟,这种延迟将直接导致导航请求的延迟,进而增加了页面的整体渲染时间。

上文中我们提到了导航请求,这里我们先简单了解下相关概念,在 Fetch 规范中的定义为:请求实体为 document 的请求。通俗来讲就是当我们在浏览器的地址栏中输入网址,或通过链接等手段从一个页面跳转到另外一个页面时所发送的请求。由于导航请求响应中的 HTML 负责启动所有脚本、样式、图片等资源的请求,因此任何导航请求的延迟都终将导致空白页问题的出现。
正是为了解决因 Service Worker 启动而导致导航请求的延迟问题,Service Worker 提供了导航预加载机制,该机制在 Service Worker 开始启动时,便立刻发起导航请求,这样 Service Worker 启动便能与导航请求并行执行(如下图),从而大大降低了因延迟而导致空白页的几率。

# 使用
导航预加载的使用非常简单,首先在
Service Worker的activate事件中启用该功能:
self.addEventListener('activate', event => {
event.waitUntil((async () => {
if (self.registration.navigationPreload) {
await self.registration.navigationPreload.enable();
}
})());
});
@前端进阶之旅: 代码已经复制到剪贴板
然后在
Service Worker的fetch事件中将预加载的导航请求响应返回即可:
self.addEventListener('fetch', event => {
const { request } = event;
if (request.method.toLowerCase() === 'get') {
event.respondWith((async () => {
//...其他类型请求处理逻辑
const preloadResponse = await event.preloadResponse;
if (preloadResponse) {
return preloadResponse;
}
})());
}
});
@前端进阶之旅: 代码已经复制到剪贴板
- 需要注意的是:如果开启了导航预加载,那么在
fetch事件中必须对event.preloadResponse进行消费,否则这将导致该请求会被请求两次。 - 导航预加载请求中会携带请求头
Service-Worker-Navigation-Preload,且默认值为true,可通过以下方式来修改其默认值:
