美文网首页
service worker 介绍

service worker 介绍

作者: 菜鸡前端 | 来源:发表于2021-09-07 23:22 被阅读0次

    service worker 是运行在浏览器后台的 JS 程序,可以操控浏览器端的存储、网络请求、消息推送等。特点:

    • 基于 web worker(一个独立于JS主线程的线程,在里面执行耗时操作不会堵塞主线程)
    • 在 web worker 的基础上增加了离线缓存的能力;
    • 本质上充当服务器与浏览器之间的代理服务器,可以拦截全站的请求并响应;
    • 由事件驱动,具有生命周期,包含 register / install / activate 等阶段;
    • 只能在 https 域 或者 localhost 下使用;
    • 支持降级处理,客户端不支持 service worker 时,对流程没有任何影响;

    1. 使用步骤

    把 service worker 看作一个代理服务器,通过例子直观的感受它。

    1.1 注册 service worker 任务

    if ('serviceWorker' in navigator) {
      window.addEventListener('load', function () {
        navigator.serviceWorker.register('sw.js').then(function (registration) {
          console.log('ServiceWorker registration successful with scope: ', registration.scope);
        }, function (err) {
          console.log('ServiceWorker registration failed: ', err);
        });
      });
    }
    

    1.2 处理安装逻辑

    // 监听安装事件
    // install 事件一般是被用来设置你的浏览器的离线缓存逻辑
    this.addEventListener("install", function (event) {
      // event.waitUntil(Promise) 保证缓存动作完成后,才会进入 installed 状态
      // 该方法会等待 Promise resolved
      event.waitUntil(
        // 打开 key 为 pwa-static 的缓存
        caches.open("pwa-static").then(function (cache) {
                // cache.addAll 指定要缓存的内容,地址为相对于跟域名的访问路径
          return cache.addAll(["./index.html"]);
        })
      );
    });
    

    监听 install 事件,当 install 事件触发时,进入 installing 阶段,使用 event.waitUntil() 保证 index.html 被缓存之后才进入 installed 状态。caches.open 用于打开一个缓存,cache.addAll 用于添加需要缓存的文件,参数为url列表。如果 event.waitUntil 接收的 promise 发生错误,则会导致 install 失败。比如添加一个不存在的文件地址。

    1.3 监听 fetch 事件

    // 注册fetch事件, 拦截请求, 可以拦截所有请求
    this.addEventListener('fetch', function(event) {
      // 在缓存中匹配对应请求资源直接返回
      event.respondWith(caches.match(event.request));
    });
    

    在 service worker 中可以监听任何 http 请求,定义响应策略,上面的代码中表示从缓存中读取并响应。

    1.4 一个完整的例子

    // register service worker
    if ('serviceWorker' in navigator) {
      navigator.serviceWorker.register('/sw-test/sw.js', { scope: '/sw-test/' }).then(function(reg) {
    
        if(reg.installing) {
          console.log('Service worker installing');
        } else if(reg.waiting) {
          console.log('Service worker installed');
        } else if(reg.active) {
          console.log('Service worker active');
        }
    
      }).catch(function(error) {
        // registration failed
        console.log('Registration failed with ' + error);
      });
    }
    
    const version = 'v7';
    
    self.addEventListener('install', function(event) {
      event.waitUntil(
        caches.open(version).then(function(cache) {
          return cache.addAll([
            // '/sw-test/',
            // '/sw-test/index.html',
            '/sw-test/style.css',
            '/sw-test/app.js',
            '/sw-test/image-list.js',
            '/sw-test/star-wars-logo.jpg',
            '/sw-test/gallery/bountyHunters.jpg',
            '/sw-test/gallery/myLittleVader.jpg',
            '/sw-test/gallery/snowTroopers.jpg'
          ]);
        }).then(() => {
          return self.skipWaiting()
        })
      );
    });
    
    // 清理过期缓存
    function clearCache() {
      console.log("clearCacheclearCacheclearCacheclearCache")
      return caches.keys().then((keys) => {
        return Promise.all(
          keys.map((key) => {
            console.log(key, version)
            if (key !== version) {
              return caches.delete(key);
            }
          })
        );
      });
    }
    
    // 激活事件
    self.addEventListener("activate", (event) => {
      console.log("activate");
      const list = Promise.all([clearCache(), self.clients.claim()])
      event.waitUntil(list);
    });
    
    self.addEventListener('fetch', function(event) {
      event.respondWith(caches.match(event.request).then(function(response) {
        if (response !== undefined) {
          return response;
        } else {
          return fetch(event.request).then(function (response) {
            let responseClone = response.clone();
            return caches.open(version).then(function (cache) {
              cache.put(event.request, responseClone);
              return response;
            })
          }).catch(function (err) {
            console.log(err, 'err')
            return caches.match('/sw-test/gallery/myLittleVader.jpg');
          });
        }
      }));
    });
    

    2. API 介绍

    2.1 Cache API

    service worker 中 self.caches 暴露了访问 cache 的能力,详情见 https://developer.mozilla.org/zh-CN/docs/Web/API/Cache

    // service-worker.js
    // self.caches
    // self.caches.open(key: string)
    // caches.keys()
    // caches.delete(key: string);
    
    Cache.match(request, options)
    // 返回一个 Promise对象,resolve的结果是跟 Cache 对象匹配的第一个已经缓存的请求,找不到返回 undefined;
    
    Cache.add(request)
    // 抓取这个URL, 检索并把返回的 response 对象添加到给定的 Cache 对象。这在功能上等同于调用 fetch(), 然后使用 Cache.put() 将 response 添加到 cache 中;
    
    Cache.addAll(requests)
    // 抓取一个URL数组,检索并把返回的response对象添加到给定的Cache对象;
    
    Cache.put(request, response)
    // 同时抓取一个请求及其响应,并将其添加到给定的cache。
    
    Cache.delete(request, options)
    // 搜索key值为request的Cache 条目。如果找到,则删除该Cache 条目,并且返回一个resolve为true的Promise对象;如果未找到,则返回一个resolve为false的Promise对象。
    
    Cache.keys(request, options)
    // 返回一个Promise对象,resolve的结果是Cache对象key值组成的数组。
    

    2.2 event.waitUntil()

    查看详细文档,这个方法告诉浏览器事件一直进行,直至 promise 解决,浏览器不应该在事件中的异步操作完成之前终止服务工作线程。

    • install 事件使用 waitUntil() 来将服务工作线程保持在 installing 阶段。如果传入 waitUntil() 的 promise 被拒绝,则将此次安装视为失败,丢弃这个服务工作线程。这主要用于确保在服务工作线程安装以前,所有依赖的核心缓存都已经成功载入。
    • activate 事件使用 waitUntil() 来延迟函数事件,如 fetch 和 push,直至传入 waitUntil() 的 promise 被解决。这让服务工作线程有时间更新数据库和删除过时缓存,让其他事件能在一个完成更新的状态下进行。

    2.3 self.skipWaiting()

    当安装成功后,更新的 Worker会处于等待状态,直到现有 Worker 让出授权,另外在刷新期间客户端会重叠。
    使用 self.skipWaiting() 会跳过等待,Service Worker 在安装完后立即激活。

    3. 注意事项

    • 非 PWA 应用,客户端不要缓存 html 文件,否则更会非常的麻烦;
    • service worker 文件本身的更新,依赖 HTTP 缓存策略;
    • service worker 文件本身,服务端最好设置 cache-control: no-store 不要缓存;
    • 多次 register() 同一个 Service Worker 不会触发更新

    4. 参考文档

    1. 使用 Service Workers [ https://developer.mozilla.org/zh-CN/docs/Web/API/Service_Worker_API/Using_Service_Workers#service_workers_demo]
    2. Service Worker: 简介 [https://developers.google.cn/web/fundamentals/primers/service-workers/]
    3. MDN Cache [https://developer.mozilla.org/zh-CN/docs/Web/API/Cache]
    4. MDN fetch [https://developer.mozilla.org/zh-CN/docs/Web/API/Fetch_API/Using_Fetch]
    5. Service Worker API [https://developer.mozilla.org/zh-CN/docs/Web/API/Service_Worker_API#service_worker_%E7%9A%84%E6%A6%82%E5%BF%B5%E5%92%8C%E7%94%A8%E6%B3%95]
    6. sw-test [https://github.com/mdn/sw-test]
    7. PWA 入门: 理解和创建 Service Worker 脚本 [https://zhuanlan.zhihu.com/p/25524382]
    8. SW 更新 https://harttle.land/2017/04/10/service-worker-update.html#header-0

    相关文章

      网友评论

          本文标题:service worker 介绍

          本文链接:https://www.haomeiwen.com/subject/fksmwltx.html