Service Worker基础知识整理

作者 : 开心源码 本文共7087个字,预计阅读时间需要18分钟 发布时间: 2022-05-12 共257人阅读

Service Worker是什么

service worker 是独立于当前页面的一段运行在浏览器后端进程里的脚本。它的特性将包括推送消息,背景后端同步, geofencing(地理围栏定位),阻拦和解决网络请求。

这个 API 会让人兴奋的起因是,它可以使你的应用先访问本地缓存资源,所以在离线状态时,在没有通过网络接收到更多的数据前,仍可以提供基本的功能(一般称之为 Offline First)。

在 service worker 之前,另一个叫做 APP Cache 的 api 也可以提供离线体验。APP Cache 的的主要问题是坑比较多,而且其被设计为只适合于单页 web 应用程序,对于传统的多页网站则不适合。service worker 的设计规避了这些痛点。

关于 service worker 的少量注意点:

  • service worker 是一个JavaScript worker ,所以它不能直接访问 DOM 。但 service worker 可以通过postMessage 接口与跟其相关的页面进行通信,发送消息,从而让这些页面在有需要的时候去操纵 DOM 。
  • Service worker 是一个可编程的网络代理商,允许你去控制如何解决页面的网络请求, 可以解决fetch请求。
  • Service worker 在不使用时将被终止,并会在需要的时候重新启动,因而你不能把onfetch 和 onmessage事件来作为全局依赖解决程序。假如你需要持久话少量信息并在重新启动Service worker后使用他,可以使用 IndexedDBAPI ,service worker 支持。
  • Service Worker 的缓存机制是依赖 Cache API 实现的
  • Service worker 广泛使用了 promise。
  • Service worker依赖 HTML5 fetch API
  • Service Workers 要求必需在 HTTPS 下才能运行

Service Worker生命周期

  1. 注册service worker,在网页上生效

  2. 安装成功,激活 或者者 安装失败(下次加载会尝试重新安装)

  3. 激活后,在sw的作用域下作用所有的页面,初次控制sw不会生效,下次加载页面才会生效。

  4. sw作用页面后,解决fetch(网络请求)和message(页面消息)事件 或者者 被终止(节省内存)。

需要提前掌握的API

  1. Cache API基本使用

(1)检测api能否存在

    if('caches' in window) {        // Has support!    }

(2)caches.open,创立缓存总对象。如下创立名为 test-cache 的缓存。

    caches.open('test-cache').then(function(cache) {        // Cache is created and accessible    });

(3)cache.add和cache.addAll,增加缓存内容。其中cache.add只增加一个,cache.addAll可以增加多个。

    caches.open('test-cache').then(function(cache) {         cache.addAll(['/', '/images/logo.png'])        .then(function() {         // Cached!                // or use cache.add        cache.add('/page/1');  // "/page/1" URL will be fetched and cached!     });    });

(4)cache.keys(),查看已经缓存的数据

    caches.open('test-cache').then(function(cache) {         cache.keys().then(function(cachedRequests) {             console.log(cachedRequests); // [Request, Request]        });    });

(5)cache.match和cache.matchAll,匹配缓存文件路径

    caches.open('test-cache').then(function(cache) {        cache.match('/page/1').then(function(matchedResponse) {            console.log(matchedResponse);        });    });

(6)cache.delete,删除缓存。

    caches.open('test-cache').then(function(cache) {        cache.delete('/page/1');    });
  1. Fetch API基本使用
    (1)简单使用
// url (required), options (optional)fetch('https://davidwalsh.name/some/url', {    method: 'get'}).then(function(response) {    }).catch(function(err) {    // Error :(});

其中options对象包含以下属性:

  • method – GET, POST, PUT, DELETE, HEAD
  • url – 请求的链接
  • headers – 请求的header对象
  • referrer – 请求的referrer对象
  • mode – cors, no-cors, same-origin
  • credentials – 设置请求可不可以携带cookie
  • redirect – follow, error, manual
  • integrity – 子资源完整值
  • cache – 缓存模式 (default, reload, no-cache)

(2)可以在fetch中传入Request对象实例:

var request = new Request('https://davidwalsh.name/users.json', {    method: 'POST',     mode: 'cors',     redirect: 'follow',    headers: new Headers({        'Content-Type': 'text/plain'    })});// Now use it!fetch(request).then(function() { /* handle response */ });

(3)可以自己设置返回的Response对象实例,其中的options有:

  • type – basic, cors
  • url
  • useFinalURL – 上面的url参数是不是最终的URL
  • status – 状态码(ex: 200, 404, etc.)
  • ok – 能否成功响应 (范围在 200-299)
  • statusText – 状态码 (ex: OK)
  • headers – 响应的headers对象

另外Response的实例还具有以下方法:

  • clone() – 创立Response对象的克隆。
  • error() – 返回与网络错误关联的新Response对象。
  • redirect() – 使用不同的URL创立新响应。
  • arrayBuffer() – 返回使用ArrayBuffer解析的promise。
  • blob() – 返回使用Blob解析的promise。
  • formData() – 返回使用FormData对象解析的promise。
  • json() – 返回使用JSON对象解析的promise。
  • text() – 返回使用USVString(文本)解析的promise。
// Create your own response for service worker testing// new Response(BODY, OPTIONS)var response = new Response('.....', {    ok: false,    status: 404,    url: '/'});// The fetch's `then` gets a Response instance backfetch('https://davidwalsh.name/')    .then(function(responseObj) {        console.log('status: ', responseObj.status);    });

Service Worker的使用

  1. 兼容低版本,注入Cache API的一个polyfill,Service Worker需要依赖Cache API:
self.importScripts('./serviceworker-cache-polyfill.js');
  1. 注册service worker:
if ('serviceWorker' in navigator) {  navigator.serviceWorker.register('/sw.js').then(function(registration) {    // Registration was successful    console.log('ServiceWorker registration successful with scope: ',    registration.scope);  }).catch(function(err) {    // registration failed :(    console.log('ServiceWorker registration failed: ', err);  });}

上面的代码检查 service worker API 能否可用,假如可用, /sw.js 这个文件将会作为 service worker 被注册。

假如这个 service worker 已经被注册过,浏览器会自动忽略上面的代码。

有一个特别要注意是 service worker 文件的路径。你肯定注意到,在这个例子中,service worker 文件被放在这个域的根目录下,这意味着 service worker是跟网站同源的。换句话说,这个 service worker 将会获取到这个域下的所有 fetch 事件。假如 service worker文件注册到/example/sw.js ,那么 service worker 只能收到 /example/ 路径下的 fetch 事件(比方: /example/page1/, /example/page2/)。

  1. 安装service worker:
var CACHE_NAME = 'my-site-cache-v1';var urlsToCache = [  '/',  '/styles/main.css',  '/script/main.js'];self.addEventListener('install', function(event) {  // Perform install steps  event.waitUntil(    caches.open(CACHE_NAME)      .then(function(cache) {        console.log('Opened cache');        return cache.addAll(urlsToCache);      })  );});

上面代码公告了需要缓存的内容,假如所有的文件都缓存成功,service worker 就安装成功了。假如任何一个文件下载失败,那么安装步骤就会失败。这个方式依赖于你自己指定的资源,但这意味着,你需要非常仔细地确定哪些文件需要被缓存。指定了太多文件的话,会添加失败率。

  1. 缓存和返回请求
self.addEventListener('fetch', function(event) {  event.respondWith(    caches.match(event.request)      .then(function(response) {        // Cache hit - return response        if (response) {          return response;        }    // IMPORTANT: Clone the request. A request is a stream and    // can only be consumed once. Since we are consuming this    // once by cache and once by the browser for fetch, we need    // to clone the response    var fetchRequest = event.request.clone();    return fetch(fetchRequest).then(      function(response) {        // Check if we received a valid response        if(!response || response.status !== 200 || response.type !== 'basic') {          return response;        }        // IMPORTANT: Clone the response. A response is a stream        // and because we want the browser to consume the response        // as well as the cache consuming the response, we need        // to clone it so we have 2 stream.        var responseToCache = response.clone();        caches.open(CACHE_NAME)          .then(function(cache) {            cache.put(event.request, responseToCache);          });        return response;      }    );  }));

假如我们想在缓存中增加新的请求缓存,可以通过解决fetch请求的response,将其增加到缓存中就可。代码里我们做了以下事情:

  • 增加一个 callback 到 fetch 请求的 .then 方法中。
    一旦我们取得一个 response,我们进行如下的检查:

    • 确保 response 有效
    • 检查 response 的状态是200
    • 确保 response 的类型是 basic 类型的,这说明请求是同源的,这意味着第三方的请求不能被缓存。
  • 假如检查通过会clone 这个请求。这么做的起因是假如 response 是一个 Stream,那么它的 body 只能被消费一次。所以为了让浏览器跟缓存都使用这个body,我们必需克隆这个 body,一份到浏览器,一份到缓存中缓存。

  1. 重新激活

你的 service worker 总会有要升级的时候。在那时,你需要按照以下步骤来升级:

  • 升级你 service worker 的 JavaScript 文件
    当客户浏览你的网站时,浏览器尝试在后端重新下载 service worker 的脚本文件。经过比照,只需服务器上的文件和本地文件有一个字节不同,这个文件就认为是新的。

  • 之后升级后的 service worker 启动并触发 install 事件。

    此时,当前页面生效的仍然是老版本的 service worker,新的 service worker 会进入 “waiting” 状态。

  • 当页面关闭之后,老的 service worker 会被干掉,新的 servicer worker 接管页面
    一旦新的 service worker 生效后会触发 activate 事件。
    通常来讲,需要在 activate 的 callback 中进行 cache 管理,来清除老的 cache。我们在 activate 而不是 install 的时候进行的起因,是假如我们在 install 的时候进行清除,那么老的 service worker 依然在控制页面,他们依赖的缓存就失效了,因而就会忽然被中止。

之前我们使用的缓存可以叫 my-site-cache-v1 ,我们想把这个拆封到多个缓存,一份给页面使用,一份给博客文章使用。这意味着,install 步骤里,我们要创立两个缓存: pages-cache-v1 和 blog-posts-cache-v1。在 activite 步骤里,我们需要删除旧的 my-site-cache-v1。

下面的代码会遍历所有的缓存,并删除掉不在 cacheWhitelist 数组(我们定义的缓存白名单)中的缓存。

self.addEventListener('activate', function(event) {  var cacheWhitelist = ['pages-cache-v1', 'blog-posts-cache-v1'];  event.waitUntil(    caches.keys().then(function(cacheNames) {      return Promise.all(        cacheNames.map(function(cacheName) {          if (cacheWhitelist.indexOf(cacheName) === -1) {            return caches.delete(cacheName);          }        })      );    })  );});

具体实践

参考文章

http://kailian.github.io/2017/03/01/service-worker

https://davidwalsh.name/cache

https://juejin.im/post/5ba0fe356fb9a05d2c43a25c

https://developer.mozilla.org/zh-CN/docs/Web/API/Service_Worker_API/Using_Service_Workers

https://lavas.baidu.com/doc/offline-and-cache-loading/service-worker/how-to-use-service-worker

https://davidwalsh.name/cache

https://zhuanlan.zhihu.com/p/20040372

https://zhuanlan.zhihu.com/p/28161855

https://www.villainhr.com/page/2017/01/08/Service%20Worker%20%E5%85%A8%E9%9D%A2%E8%BF%9B%E9%98%B6

说明
1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是摆设,本站源码仅提供给会员学习使用!
7. 如遇到加密压缩包,请使用360解压,如遇到无法解压的请联系管理员
开心源码网 » Service Worker基础知识整理

发表回复