通过
Service Worker的fetch事件可监听页面中的所有请求,因此可通过缓存构建响应,以减少请求的响应时间。Workbox中的workbox-routing模块为我们提供了便捷的方式来匹配并处理请求,本章我们将对该模块进行详细介绍。
# 基本使用
我们通过调用
workbox.routing.registerRoute方法来注册并处理请求,方法参数依次为:
capture:请求匹配规则,类型为字符串、正则表达式、函数或workbox.routing.Route。handler:请求处理函数,返回值为Promise<Response>,参数为含有以下属性的对象:url:匹配到的请求地址,类型为:URL。event:触发请求的 FetchEvent 对象,该属性为可选属性。request:触发请求的 Request 对象,该属性为可选属性。params:请求匹配函数的返回值,类型为非空数组、非空对象或undefined。- 注:该参数的值亦可为含有属性
handle的对象,且属性handle的值与参数值为函数时一致。
method:请求方法,值为GET、HEAD、POST、PATCH、PUT或DELETE,默认值为GET。
下面我们通过一些示例来介绍
workbox.routing.registerRoute的使用:
- 当
capture的值为正则表达式时:
workbox.routing.registerRoute(
new RegExp('/styles/.*\\.css'),
({ url, event, request, params }) => Promise.resolve(new Response(...))
);
示例中,我们注册了
/styles路径下css文件请求的监听处理,需要注意的是,由于同源策略的影响,此时capture的值将无法匹配第三方站点的请求,比如:
https://cdn.third-party-site.com/styles/main.css
如果想要正确匹配第三方资源,只需要保证正则表达式能够与请求
URL的开头相匹配即可,因此capture的值可修改为:new RegExp('https://.*/styles/.*\\.css')
- 当
capture的值为函数时:
workbox.routing.registerRoute(
({ url, event, request }) => true,
({ url, event, request, params }) => Promise.resolve(new Response(...))
);
如果
capture的返回值为truthy,则立刻调用handler处理函数,如果返回值为非空数组或非空对象,返回值将以 params 的形式传递给 handler 处理函数。函数的参数为含有以下属性的对象:
url:匹配到的请求地址,类型为:URL。event:触发请求的FetchEvent对象,该属性为可选属性。request:触发请求的Request对象,该属性为可选属性
- 当
capture的值为workbox.routing.Route时:
workbox.routing.registerRoute(
new workbox.routing.Route(
({ url, event, request }) => true,
({ url, event, request, params }) => Promise.resolve(new Response(...))
);
);
- 当
capture的值为workbox.routing.Route时,参数handler、method将被忽略,故无需设置其值。
workbox.routing.Route的参数依次为match、handler和method:
match等同于workbox.routing.registerRoute方法中的capture参数(值为函数时)。handler、method等同于workbox.routing.registerRoute方法中的handler、method参数。
# 全局处理函数
上文我们对
workbox.routing.registerRoute方法的使用进行了介绍,然而有时我们可能需要处理未被成功匹配的请求,此时便可通过以下方式来设置默认处理函数:
workbox.routing.setDefaultHandler(({ url, event, request, params }) => {
return Promise.resolve(new Response(...));
});
在另外的一些场景下,如果注册的路由抛出异常,可能需要捕获异常并做一些降级处理(比如网络异常后通过缓存构建响应),可通过以下方式进行处理:
workbox.routing.setCatchHandler(({ url, event, request, params }) => {
return Promise.resolve(new Response(...));
});
方法
workbox.routing.setDefaultHandler和workbox.routing.setCatchHandler的参数与上文中所讨论的handler处理函数的使用一致,此处不再重述。
# 高级使用
同预缓存一样,我们不仅可以通过
workbox.routing.registerRoute来快速注册请求响应,也可通过workbox.routing.DefaultRouter来自行接管Service Worker的 fetch 事件,比如:
const router = new workbox.routing.DefaultRouter();
router.registerRoute(new Route(matchCb, handlerCb));
router.registerRoute(new RegExpRoute(new RegExp(...), handlerCb));
self.addEventListener('fetch', event => {
const responsePromise = router.handleRequest(event);
if (responsePromise) {
event.respondWith(responsePromise);
} else {
// 不匹配后的逻辑处理
}
});
注:
workbox.routing.DefaultRouter的实例方法registerRoute参数必须为workbox.routing.Route类型。
# 注意事项
由于在
Workbox中,会按照路由注册的先后顺序对请求进行匹配(亦包含通过workbox.precaching.precacheAndRoute注册的预缓存路由),一旦有路由匹配到该请求,便利用该路由进行处理。对该规则处理不当,很可能会造成意想不到的结果,比如:
workbox.routing.registerRoute(
new RegExp('/styles/.*\\.css'),
handlerCb
);
workbox.routing.registerRoute(
'/styles/example.ac29.css',
handlerCb1
);
上例中,我们的本意是想使用
handlerCb1来响应/styles/example.ac29.css请求,但由于该请求亦能被handlerCb处理,且此路由优先于handlerCb1注册,所以实际上请求由handlerCb进行响应。为避免此类错误的出现,我们应该按照匹配范围从小到大的顺序进行路由规则定义。
# 总结
本章我们对
workbox-routing模块的使用及使用过程中可能存在的问题进行了详细介绍,通过该模块,我们具备了拦截并处理页面中所有请求的能力,但仍需利用实战篇中讨论的请求策略、缓存置换等机制来更高效地完成请求的响应,因此下一章,我们将对 Workbox 中的请求策略、缓存置换进行讨论。