通过 ServiceWorker 可以拦截前端的请求,而 Cache 可以根据请求将响应存储在前端,那么这两者结合起来我们就可以实现一个前端的缓存方案了,这里我们来讨论一下具体的实现和几种缓存策略。
网络请求优先策略

(图片来源:https://huangxuan.me/2016/11/20/sw-101-gdgdf/,侵删)
这个方案优先使用网络请求,网络请求失败后再从缓存中提取数据,记得在网络请求后及时更新缓存。不好的地方显而易见,并没有大幅提高用户体验和速度。
1 2 3 4 5 6 7 8 9
| self.addEventListener('fetch', e => { e.respondWith( fetch(e.request.url) .catch(e => { return caches.match(e.request) .then(res => res || e); }) ); });
|
缓存优先策略

(图片来源:https://huangxuan.me/2016/11/20/sw-101-gdgdf/,侵删)
这个策略优先使用缓存,缓存没有命中或失效才发送网络请求,这个方案会造成你的用户很难看到最新的响应,如果场景适合这个方案千万记得做好版本控制。
1 2 3 4 5 6
| self.addEventListener('fetch', e => { e.respondWith( caches.match(e.request.url) .then(res => res || fetch(e.request)) ); });
|
请求和缓存竞赛

(图片来源:https://huangxuan.me/2016/11/20/sw-101-gdgdf/,侵删)
这个方案在性能上胜于其他方案,只是要额外多一个操作,并且结果是不可控的。
1 2 3 4 5 6 7 8
| self.addEventListener('fetch', e => { e.respondWith( Promise.race([ fetch(e.request.url), caches.match(e.request) ]) ); });
|
超时后重新请求策略

(图片来源:https://huangxuan.me/2016/11/20/sw-101-gdgdf/,侵删)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| self.addEventListener('fetch', e => { let fetchData = fetch(`${ e.request.url }?${ Date.now() }`); let _fetchData = fetchData.then(raw => raw.clone()); e.respondWith( caches.match(e.request) .then(res => res || fetchData) ); e.waitUntil( Promise.all([ _fetchData, caches.open('test-1.0') ]).then(([res, cache]) => { if (res.ok) cache.put(e.reqeust, res); }); ); });
|
最快的超时后重新请求策略
其实就是结合竞速啦:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| self.addEventListener('fetch', e => { let fetchData = fetch(`${ e.request.url }?${ Date.now() }`); let _fetchData = fetchData.then(raw => raw.clone()); e.respondWith( Promise.race([fetchData, caches.match(e.request)]) .then(res => res || fetchData) ); e.waitUntil( Promise.all([ _fetchData, caches.open('test-1.0') ]).then(([res, cache]) => { if (res.ok) cache.put(e.reqeust, res); }); ); });
|