什么是workbox,workbox有什么用途,为何要使用它?在介绍workbox以前,咱们来先大体了解一下service worker,有助于咱们后面更好地去理解workbox。css
service worker是在浏览器后台独立于网页运行的脚本,它可以实现对网络请求进行缓存,并向网页推送和同步信息的功能,使人更加兴奋的是,它能够实现离线的状况下,也能看到咱们的网页,极大提高了咱们的用户体验。html
service worker 已经获得愈来愈多的浏览器的支持,包括苹果、腾讯的X5内核。苹果从safari11开始,已经开始支持了。支持状况以下:前端
workbox 是 GoogleChrome 团队推出的一套 Web App 静态资源和请求结果的本地存储的解决方案,该解决方案包含一些 Js 库和构建工具,在 Chrome Submit 2017 上首次隆重面世。而在 workbox 背后则是 Service Worker 和 Cache API 等技术和标准在驱动。在 Workebox 以前,GoogleChrome 团队较早时间推出过 sw-precache 和 sw-toolbox 库,可是在 GoogleChrome 工程师们看来,workbox 才是真正能方便统一的处理离线能力的更完美的方案,因此中止了对 sw-precache 和 sw-toolbox 的维护。那workbox能解决什么问题呢?webpack
在service worker中,若是咱们要拦截并代理全部的请求,须要咱们手动去维护一套缓存列表。可是如今前端开发,多数用webpack、gulp、grant来构建前端的代码,致使咱们的文件名可能会常常发生,这个时候,特别是中大型的多页应用,缓存列表的内容可能会很是多,手动维护就显得很是麻烦,维护成本也变得很高。web
这个时候,workbox的横空出世,就是为了解决上面的问题。gulp
下面来看下workbox的例子。api
1.在入口页面的onload中,注册一个service worker,注册时引入缓存列表文件,也就是build.sw.js。
index.html数组
<script> // Register A service worker if ('serviceWorker' in navigator) { window.addEventListener('load', function() { navigator.serviceWorker.register(`./build.sw.js`) .then(function(registration) { // Registration was successful console.log('[success] register ') }, function(err) { // registration failed :( console.log('[fail]: ', err); }); }); <script> }
2.在build.sw.js页面配置缓存列表和缓存策略浏览器
// 首先引入 Workbox 框架 importScripts('https://storage.googleapis.com/workbox-cdn/releases/3.3.0/workbox-sw.js'); // 注册成功后要当即缓存的资源列表 workbox.precaching.precacheAndRoute([ { "url": "css/index.css", "revision": "835ba5c3" }, { "url": "images/xxx.png", "revision": "b1537bfs" }, { "url": "index.html", "revision": "b331f695" }, { "url": "js/index.js", "revision": "4d562866" } ]); // 缓存策略 workbox.routing.registerRoute( new RegExp(''.*\.html'), workbox.strategies.networkFirst() ); workbox.routing.registerRoute( new RegExp('.*\.(?:js|css)'), workbox.strategies.cacheFirst() ); workbox.routing.registerRoute( new RegExp('https://your\.cdn\.com/'), workbox.strategies.staleWhileRevalidate() ); workbox.routing.registerRoute( new RegExp('https://your\.img\.cdn\.com/'), workbox.strategies.cacheFirst({ cacheName: 'example:img' }) );
实现的效果以下:缓存
咱们来看下build.sw.js文件的内容,主要包含缓存列表和缓存策略。这里面的内容不用咱们手动生成,workbox有三种方式生成,咱们可使用workbox-webpack-plugin、workbox-cli、workbox-build。咱们暂不讨论具体的实现,在这里,咱们先来了解一下预缓存列表和缓存策略这两个东西。
若是咱们要缓存静态资源,平时不会常常更新,只有到发版时才会修改了资源的hash值,才须要从新更新的,那那 precache 预缓存应该是你所期待的。
workbox 提供了一种很是方便的 API 帮助咱们解决 precache 的问题,咱们可使用workbox.precaching来配置,配置格式以下:
workbox.precaching.precacheAndRoute([ { "url": "将要预缓存的文件 URL", "revision": "缓存的hash值" }, ])
路由请求缓存是指经过对匹配路由给文件采起不用的缓存方式,这个能够经过workbox.routing.registerRoute来进行配置。 路由匹配的方式有三种:
1.经过字符串的方式进行匹配
// 能够直接是当前项目下的绝对路径 workbox.routing.registerRoute( 'path/to/logo.png', handler // handler 是作缓存策略的回调函数,一般指后面所会降到的 '缓存策略函数' ); // 也能够是完整的带完整 host 的 URL 路径,这里的 URL 必须是 https 的 workbox.routing.registerRoute( 'https://example.com/a/b/c.jpg', handler );
2.经过正则的方式进行匹配
workbox.routing.registerRoute( new RegExp('.*\.(js|css|jpg|png|gif)'), // 这里是任何正则都行,只要能匹配得上的请求路由地址 handler );
3.经过回调函数的方式进行匹配
// 经过回调函数来匹配请求路由将会让策略更加灵活 const customFun = ({url, event}) => { // 若是请求路由匹配了就返回true,也能够返回一个参数对象以供 handler 接收处理 return false; }; workbox.routing.registerRoute( customFun, handler );
缓存策略是指对于匹配到的路由,采起何种方式进行缓存。 workbox提供了两种配置缓存策略的方式
如下介绍workbox默认提供的几种缓存策略,包含有五种,分别是:
这种策略的意思是当请求的路由有对应的 Cache 缓存结果就直接返回,在返回 Cache 缓存结果的同时会在后台发起网络请求拿到请求结果并更新 Cache 缓存,若是原本就没有 Cache 缓存的话,直接就发起网络请求并返回结果。 使用方式以下:
workbox.routing.registerRoute( match, // 匹配的路由 workbox.strategies.staleWhileRevalidate() );
这种策略就是当请求路由是被匹配的,就采用网络优先的策略,也就是优先尝试拿到网络请求的返回结果,若是拿到网络请求的结果,就将结果返回给客户端而且写入 Cache 缓存,若是网络请求失败,那最后被缓存的 Cache 缓存结果就会被返回到客户端 使用方式以下:
workbox.routing.registerRoute( match, // 匹配的路由 workbox.strategies.networkFirst() );
这个策略的意思就是当匹配到请求以后直接从 Cache 缓存中取得结果,若是 Cache 缓存中没有结果,那就会发起网络请求,拿到网络请求结果并将结果更新至 Cache 缓存,并将结果返回给客户端。
workbox.routing.registerRoute( match, // 匹配的路由 workbox.strategies.cacheFirst() );
比较直接的策略,直接强制使用正常的网络请求,并将结果返回给客户端,这种策略比较适合对实时性要求很是高的请求。
workbox.routing.registerRoute( match, // 匹配的路由 workbox.strategies.networkOnly() );
这个策略也比较直接,直接使用 Cache 缓存的结果,并将结果返回给客户端,这种策略比较适合一上线就不会变的静态资源请求。
workbox.routing.registerRoute( match, // 匹配的路由 workbox.strategies.cacheOnly() );
在咱们的项目中,咱们以DomContentLoaded的时间做为参考点,对比有加service worker 和未加的service worker状况。
测试条件
以首页为例,在不一样的网络环境下,发起10次网络请求,而后取平均值,做为它们的最终结果,测试结果以下:
经过上面的数据能够得出几个结论:
在使用workbox的过程当中,会遇到一些问题,下面列出几点,也算是作个总结:
在页面注册service worker的时候,尽可能注册到项目的根目录下,这样才能最大的发挥service worker的做用
// build.sw.js最好放在项目的根目录下,才能发挥最大的缓存效果 navigator.serviceWorker.register(`./build.sw.js`) // 若是这样配置的话,就只有path目录下面的文件才能实现缓存,其余目录,包括根目录的都不能缓存 navigator.serviceWorker.register(`./path/build.sw.js`)
咱们先预设一下应用场景:假设你的项目在目录 /app 下,必须保证在你的项目根目录下有一个 app/sw.js 包含如下内容:
// 一般项目中的 sw.js 源文件都是经过这样预留一个空数组的方式来预缓存内容列表的 workbox.precaching.precacheAndRoute([]);
这样才能保证能将生成的预缓存内容列表内容注入到 Service Worker 文件中。
在通过一段时间的使用和思考之后,给出认为较为合理的缓存策略:
若是你们在使用过程当中有更友好的策略,麻烦也贡献大家的策略,你们共同窗习,共同进步。
还有,要牢记,对于不在同一域下的任何资源,绝对不能使用 Cache only 和 Cache first。
须要注意的是,Service Worker 脚本除了域名为 localhost 时能运行在 http 协议下之外,只能运行 https 协议下。
Google对web的标准化仍是遵循的,SW认为POST请求就是象服务器提交资源,不存在缓存需求
参考文档: