Edeity's Blog

Workbox3初探:让离线缓存变得简单

鉴于国内PWA比较鸡肋,此博客关闭了PWA,文中部分效果已失效

我曾在Https你的博客中简单尝试了PWA的离线缓存。

因为水平有限,在书写service-worker过程中,给我留下了两点不好的印象:

  1. 需要手写所有需要缓存的文件
  2. 不严谨的代码逻辑,可能导致页面更新不及时(!!包括HTML!!

随后知道了sw-precache(可以自动生成缓存文件),功能虽然强大,配置却相对麻烦。故觉得PWA有点鸡肋(好吧,我承认,是我智商不够)。直到我遇上了workbox3。像是在库海茫茫中遇见了那个“它”,是那么的简单,那么的粗暴

直接撸代码

启用最基本的Workbox3配置,仅需传入两个参数:

  • 缓存的文件(正则表达式匹配)
  • 采取的缓存策略
// 缓存 *.js、*.css、*.html
workbox.routing.registerRoute(
/.*\.(?:js|css|html).*$/,
workbox.strategies.networkFirst()
);

只需寥寥几行代码,即能赋予站点离线浏览的能力。

什么是缓存策略

用正则匹配缓存文件,自不用多说。

缓存策略则需要解析一下,Workbox3的缓存策略分为五类,分别是: Stale While RevalidateNetwork First(网络优先)、Cache First(缓存优先)、Network Only(仅网络可用)、Cache Only(仅缓存可用)。本质就是获取资源,优先显示最新代码,还是缓存代码。

Stale While Revalidate略为特殊,它和Cache First接近,均优先从缓存中读取文件。不同之处在于,Cache First一旦从缓存中获取文件,则不再从服务端请求最新代码;而Stale While Revalidate,尝试从缓存获取代码并显示执行,但无论缓存命中与否,均请求网络以更新缓存

故优点是:

  • 响应及时(优先执行缓存代码,无需等待网络请求返回)
  • 保证缓存较新(网络请求返回后,及时更新本地缓存,以备下次之需)。

小打小闹的话,推荐使用Stale While Revalidate;

拓展

错误处理

网络不畅时,希望提供比原生404错误更友好的页面。

参考github上的讨论,仅需:

  • 首次成功加载页面时,缓存离线资源(eg:img_load_fail.pngoffline.html等)

  • 后续网络不畅时,通过catch捕获workbox的网络错误,返回之前缓存的离线资源

如:

  • 访问↓↓未缓存的图片↓↓(在此通过不存在的图片进行模拟)我是不存在的图片

或:

在确保无网络后(可在Chrome的Network中勾选offline),刷新本页面,查看图片或点击链接,将看到失效的图片或链接,展示为离线资源。

效果如下:

离线访问图片

访问图片:在线 Vs离线

离线访问未访问的网站

访问网站:在线 VS 离线
代码如下:
// service-worker.js

// 离线访问未缓存的页面时,显示的页面
const HTML_FAIL_URL = '/public/html/offline.html';
// 离线访问未加载的图片
const IMG_FAIL_URL = '/public/img/img_load_fail.png';

// 处理为访问过的网页URL-HTML
let networkFirst = workbox.strategies.networkFirst({
cacheName: 'outside-pages'
});
const offlineHandler = async (args) => {
try {
const response = await networkFirst.handle(args);
return response || await caches.match(HTML_FAIL_URL);
} catch (error) {
return await caches.match(HTML_FAIL_URL);
}
};
workbox.routing.registerRoute(
/.*\.(?:html|php).*$/,
offlineHandler
);

// 访问不存在的图片-IMAGE
const imagesHandler = workbox.strategies.cacheFirst();
workbox.routing.registerRoute(
/.*\.(?:png|jpg|jpeg|svg|gif).*$/,
({event}) => {
return imagesHandler.handle({event})
.catch(() => {
return caches.match(IMG_FAIL_URL)
});
}
);

我博客sw的完整代码:service-worker.js

其他

离线提示

通过监听网络状态,在用户离线瞬间,提示用户:暂时无法连接网络

具体方法参考在线和离线事件。在此不展开。

犯下的小小错误

  1. service-worker.js需放置在根目录下,否则不生效

参考文章