VUE开发--Service Worker(五十二)

一、Service Worker

W3C 组织早在 2014 年 5 月就提出过 Service Worker 这样的一个 HTML5 API ,主要用来做持久的离线缓存。
Service Worker 有以下功能和特性:

  1. 一个独立的 worker 线程,独立于当前网页进程,有自己独立的 worker context;
  2. 一旦被 install,就永远存在,除非被 uninstall;
  3. 需要的时候可以直接唤醒,不需要的时候自动睡眠(有效利用资源,此处有坑);
  4. 可编程拦截代理请求和返回,缓存文件,缓存的文件可以被网页进程取到(包括网络离线状态);
  5. 离线内容开发者可控;
  6. 能向客户端推送消息;
  7. 不能直接操作 DOM;
  8. 出于安全的考虑,必须在 HTTPS 环境下才能工作;
  9. 异步实现,内部大都是通过 Promise 实现;
    所以我们基本上知道了 Service Worker 的伟大使命,就是让缓存做到优雅和极致,让 Web App 相对于 Native App 的缺点更加弱化,也为开发者提供了对性能和体验的无限遐想。

由于 Workers 与主线程分开运行,因此 Service Worker 独立于与其关联的应用程序。这将导致一下结果:
由于 Service Worker 没有阻塞(它被设计为完全异步),同步 XHR 和localStorage 不能在 Service Worker 中使用。
当应用程序处于没有活动状态时,Service Worker 可以从服务器接收推送消息。这可以让您的应用程序向用户显示推送通知,即使它未在浏览器中打开。

  • 注意 :浏览器在没有运行的情况下是否能收到通知取决于浏览器如何与操作系统集成。例如,在桌面操作系统上,Chrome 浏览器和 Firefox 只会在浏览器运行时收到通知。然而,Android 是在接收到推送消息时唤醒任何浏览器,并且无论浏览器状态如何都将始终接收推送消息。
    Service Worker 不能直接访问 DOM。为了与页面通信,需使用 postMessage() 方法发送数据,并使用 message 事件侦听器来接收数据。
  • Service Worker 注意事项:
    Service Worker 是一个可编程的网络代理,可以控制如何处理来自页面的网络请求。
    Service Worker 只能通过 HTTPS 运行。由于 Service Worker 可以拦截网络请求并修改响应,因此会带来非常糟糕的 "man-in-the-middle" 攻击。
  • 注意:像 Letsencrypt 这样的服务可让您免费获取 SSL 证书以安装到您的服务器上。
    Service Worker 在不使用时变为空闲状态,并在下次需要时重新启动。你不能依赖事件之间持续存在的全局状态。如果存在需要在重新启动时保留和重用的信息,则可以使用 IndexedDB 数据库。
    Service Worker 广泛使用 Promises,所以如果你对 Promises 不熟悉,那么你应该停止阅读并开始学习 Promises 的介绍。

二、Service Worker 生命周期

  1. 注册
    Registration
    要安装 Service Worker,您需要在 JavaScript 主进程中进行注册。注册时需要告诉浏览器您的 Service Worker 所在的位置,然后在后台安装它。如:
    main.js
if ('serviceWorker' in navigator) {
    navigator.serviceWorker.register('/service-worker.js').then(function (registration) {  
    console.log('Registration successful, scope is:', registration.scope);  
  }).catch(function (error) {  
    console.log('Service Worker registration failed, error:', error);  
  });  
}

registration.scope 决定 Service Worker 可以控制哪些文件,换句话说 Service Worker 将从哪个路径拦截请求。默认的范围是 Service Worker 文件的位置,并扩展到以下所有目录。因此,如果 service-worker.js 位于根目录中,则服务工作人员将控制来自该域中所有文件的请求。
当然您还可以在注册时通过传入附加参数来设置任意范围。例如:

main.js

navigator.serviceWorker.register('/service-worker.js',  {  
  scope:  '/app/'  
});  
  1. 安装
    Installation
    一旦浏览器注册了 Service Worker,Installation就会被触发。以下情况都会触发 Installation:
  • Service Worker 被浏览器认为是新的
  • 该站点当前没有注册过 Service Worker
  • 新的 Service Worker 和先前安装的 Service Worker 之间存在字节差异
    Service Worker Installation 会在 Service Worker installing 过程中触发 install 事件。 我们可以在 Service Worker 监听 install 事件,以便在 Service Worker 安装时执行一些任务。
    例如,在安装过程中,Service Worker 可以预先缓存 Web 应用程序的某些部分,以便在用户下次打开应用程序时立即加载它(请参阅
    caching the application shell)。所以,在第一次加载之后,后面的重复加载都会被缓存,这样,交互性上的时间将会缩短。监听示例如下:
    service-worker.js
self.addEventListener('install',  function(event)  { 
  // Perform some task  
});  
  1. 激活
    Activation
    一旦 Service Worker 成功安装,它将转换到Activation阶段。如果以前的 Service Worker 还在服务着任何打开的页面,则新的 Service Worker 进入 waiting 状态。新的 Service Worker 仅在旧的 Service Worker 没有任何页面被加载时激活。这确保了在任何时间内只有一个版本的 Service Worker 正在运行。
    注意一般的页面刷新不会将控制权转移给新的 Service Worker,因为刷新之前新页面并不会被加载,整个过程中旧的 Service Worker 将会一直被使用。
    当新的 Service Worker 激活时,activate 事件将被触发。此事件侦听器可以用来清理过时的缓存(请参阅 Offline Cookbook 中的示例)。
    service-worker.js
self.addEventListener('activate',  function(event)  { // Perform some task  
});  

激活后,Service Worker 将控制加载在其范围内的所有页面,并开始监听来自这些页面的事件。但是,在 Service Worker 激活之前加载的页面不在 Service Worker 控制之下。当您关闭并重新打开您的应用程序时,或者 Service Worker 调用 clients.claim 时,新的 Service Worker 才会生效。在此之前,来自该页面的请求将不会被新的 Service Worker 拦截。这是可以保证您网站的一致性。

三、具体应用

  1. Workbox
    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 的维护。

  2. 不管你的站点是何种方式构建的,都可以为你的站点提供离线访问能力;

  3. 就算你不考虑离线能力,也能让你的站点访问速度更加快;

  4. 几乎不用考虑太多的具体实现,只用做一些配置;

  5. 简单却不失灵活,可以完全自定义相关需求(支持 Service Worker 相关的特性如 Web Push, Background
    sync 等);

  6. 针对各种应用场景的多种缓存策略。

  7. 用法
    想要使用 workbox,首先需要在你的项目中创建一个 Service Worker 文件 sw.js 并且在你的站点上注册:


有了 sw.js 之后就可以使用 workbox 了,你只需要在 sw.js 中导入 workbox 就可以使用了:

importScripts('https://storage.googleapis.com/workbox-cdn/releases/3.0.0-alpha.3/workbox-sw.js');
if (workbox) {
    console.log(`耶! workbox已加载`);
}
else {
    console.log(`不! workbox没有加载`);
}

比较完整的 sw.js:https://wangdaodao.com/sw.js

'use strict';
//使用阿里的CDN
importScripts('https://g.alicdn.com/kg/workbox/3.3.0/workbox-sw.js');
workbox.setConfig({
    modulePathPrefix: 'https://g.alicdn.com/kg/workbox/3.3.0/'
});
if (workbox) {
    console.log(`Yay! Workbox is loaded`);
} else {
    console.log(`Boo! Workbox didn't load`);
}
workbox.routing.registerRoute(
    // Cache CSS files
    /.*\.css/,
    // 使用缓存,但尽快在后台更新
    workbox.strategies.staleWhileRevalidate({
        // 使用自定义缓存名称
        cacheName: 'css-cache',
    })
);
workbox.routing.registerRoute(
    // 缓存JS文件
    /.*\.js/,
    // 使用缓存,但尽快在后台更新
    workbox.strategies.staleWhileRevalidate({
        // 使用自定义缓存名称
        cacheName: 'js-cache',
    })
);
workbox.routing.registerRoute(
    // 缓存gravatar文件
    new RegExp('https://cdn\.v2ex\.com/'),
    // 如果缓存可用,请使用它
    workbox.strategies.cacheFirst({
      // 使用自定义缓存名称
      cacheName: 'gravatar-cache',
      plugins: [
        new workbox.expiration.Plugin({
          // 缓存最多30天
          maxAgeSeconds: 30 * 24 * 60 * 60,
        })
      ],
    })
);

workbox.strategies 对象为我们提供了几种最常用的策略:
Stale-While-Revalidate


Stale-While-Revalidate

Cache First


Cache First

Network First
Network First

Network Only
Network Only

Cache Only


Cache Only
  • 注意事项
    如果你想让页面离线可以访问,使用 NetworkFirst,如果不需要离线访问,使用 NetworkOnly,其他策略均不建议对 HTML 使用。
    CSS 和 JS情况比较复杂,因为一般站点的 CSS,JS 都在 CDN 上,SW 并没有办法判断从 CDN 上请求下来的资源是否正确(HTTP 200),如果缓存了失败的结果,问题就大了。这种我建议使用 Stale-While-Revalidate 策略,既保证了页面速度,即便失败,用户刷新一下就更新了。如果你的 CSS,JS 与站点在同一个域下,并且文件名中带了 Hash 版本号,那可以直接使用 Cache First 策略。
    图片建议使用 Cache First,并设置一定的失效事件,请求一次就不会再变动了。
    对于不在同一域下的任何资源,绝对不能使用 Cache only 和 Cache first。

你可能感兴趣的:(VUE开发--Service Worker(五十二))