Service Worker

作者: 分析 | 来源:发表于2017-03-01 22:44 被阅读5887次

    简书放不了demo,demo可以看原文

    Service Worker 是什么?

    service worker 是独立于当前页面的一段运行在浏览器后台进程里的脚本。
    service worker不需要用户打开 web 页面,也不需要其他交互,异步地运行在一个完全独立的上下文环境,不会对主线程造成阻塞。基于service worker可以实现消息推送,静默更新以及地理围栏等服务。
    service worker提供一种渐进增强的特性,使用特性检测来渐渐增强,不会在老旧的不支持 service workers 的浏览器中产生影响。可以通过service workers解决让应用程序能够离线工作,让存储数据在离线时使用的问题。

    注意事项:
    1.service worker运行在它们自己的完全独立异步的全局上下文中,也就是说它们有自己的容器。
    2.service worker没有直接操作DOM的权限,但是可以通过postMessage方法来与Web页面通信,让页面操作DOM。
    3.service worker是一个可编程的网络代理,允许开发者控制页面上处理的网络请求。
    4.浏览器可能随时回收service worker,在不被使用的时候,它会自己终止,而当它再次被用到的时候,会被重新激活。
    5.service worker的生命周期是由事件驱动的而不是通过Client。

    Service Worker生命周期

    service worker拥有一个完全独立于Web页面的生命周期

    sw-lifecycle.png
    1. 注册service worker,在网页上生效
    2. 安装成功,激活 或者 安装失败(下次加载会尝试重新安装)
    3. 激活后,在sw的作用域下作用所有的页面,首次控制sw不会生效,下次加载页面才会生效。
    4. sw作用页面后,处理fetch(网络请求)和message(页面消息)事件 或者 被终止(节省内存)。

    Service Worker支持使用

    浏览器支持

    service worker support

    navigator-serviceworker.png

    polyfill

    使用ServiceWorker cache polyfill让旧版本浏览器支持 ServiceWorker cache API,

    https

    Server需要支持https

    通过service worker可以劫持连接,伪造和过滤响应,为了避免这些问题,只能在HTTPS的网页上注册service workers,防止加载service worker的时候不被坏人篡改。

    Github Pages是HTTPS的,可以通过Github做一些尝试

    调试工具

    在调试的时候可以用于unregister、stop、start等操作

    chrome访问chrome://inspect/#service-workerschrome://serviceworker-internals查看service-workers

    chrome-tool-1.png chrome-tool-2.png

    firefox通过about:debugging#workers查看

    firefox-tool.png

    更多请参考debugging-service-workers

    离线存储数据

    对URL寻址资源,使用Cache API。对其他数据,使用IndexedDB。

    离线阅读

    demo

    使用https访问本文,打开ChromeDevTools,选择Application选项卡->Service Workers

    sw-devtools.png

    可以看到Service Workers注册

    点击下面离线保存按钮

    <button class="btn btn-sm btn-primary offline-btn">离线保存</button>

    然后选择Cache Storage,可以看到文字内容已经缓存到Cache Storage

    sw-cache.png

    然后选择Service Workers 勾选 Offline,NetWork出现了⚠️️,然后试试离线访问本文😎

    sw-offline.png

    原理

    注册 service worker

    创建一个 JavaScript 文件(比如:sw.js)作为 service worker

    告诉浏览器注册这个JavaScript文件为service worker,检查service worker API是否可用,如果可用就注册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);
      });
    }
    

    sw.js文件被放在这个域的根目录下,和网站同源。这个service work将会收到这个域下的所有fetch事件。

    如果将service worker文件注册为/example/sw.js,那么,service worker只能收到/example/路径下的fetch事件(例如: /example/page1/, /example/page2/)。

    // Service Workers
    if ('serviceWorker' in navigator && navigator.onLine) {
      navigator.serviceWorker.register('/sw.js').then(function(registration) {
        console.log('ServiceWorker registration successful with scope: ',    registration.scope);
      }).catch(function(err) {
        console.log('ServiceWorker registration failed: ', err);
      });
    
      var currentPath = window.location.pathname;
      var cacheButton = document.querySelector('.offline-btn');
    
      var imageArray = document.querySelectorAll('img');
      if(cacheButton) {
        cacheButton.addEventListener('click', function(event) {
          event.preventDefault();
          // 缓存当前链接和使用的图片
          var pageResources = [currentPath];
          for (i = 0; i < imageArray.length; i++) {
            pageResources.push(imageArray[i].src);
          }
          caches.open('offline-' + currentPath).then(function(cache) {
            var updateCache = cache.addAll(pageResources);
    
            updateCache.then(function() {
              console.log('Article is now available offline.');
              cacheButton.innerHTML = "☺";
            });
    
            updateCache.catch(function (error) {
              console.log('Article could not be saved offline.');
              cacheButton.innerHTML = "☹";
            });
          });
        });
      }
    }
    

    缓存站点的资源

    定义需要缓存的文件,然后在sw注册安装后回到cache Api将资源文件写入缓存。如果所有的文件都被缓存成功了,那么service worker就安装成功了。如果任何一个文件下载失败,那么安装步骤就会失败。

    var cacheName = 'v1';
    var assetsToCache = [
      '/styles/main.css',
      '/script/main.js'
    ];
    
    self.addEventListener('install', function(event) {
      event.waitUntil(
        caches.open(cacheName).then(function(cache) {
          return cache.addAll(assetsToCache);
        }).then(function() {
          return self.skipWaiting();
        })
      );
    });
    

    从缓存中加载

    service worker成功注册,并且用户浏览了另一个页面或者刷新了当前的页面,service worker将开始接收到fetch事件。

    拦截网络请求并使用缓存,缓存命中,返回缓存资源,否则返回一个实时从网络请求fetch的结果。

    self.addEventListener('fetch', function(event) {
      var requestUrl = new URL(event.request.url);
      if (requestUrl.origin === location.origin) {
          if (requestUrl.pathname === '/') {
          event.respondWith(
            caches.open(cacheName).then(function(cache) {
              return fetch(event.request).then(function(networkResponse) {
                cache.put(event.request, networkResponse.clone());
                return networkResponse;
              }).catch(function() {
                return cache.match(event.request);
              });
            })
          );
        }
      }
    
      event.respondWith(
        caches.match(event.request).then(function(response) {
          return response || fetch(event.request);
        })
      );
    });
    

    缓存版本管理

    版本修改的时候会触发activate,将旧版本的缓存清理掉。

    var OFFLINE_PREFIX = 'offline-';
    var CACHE_NAME = 'main_v1.0.0';
    self.addEventListener('activate', function(event) {
      var mainCache = [CACHE_NAME];
      event.waitUntil(
        caches.keys().then(function(cacheNames) {
          return Promise.all(
            cacheNames.map(function(cacheName) {
              if ( mainCache.indexOf(cacheName) === -1 && cacheName.indexOf(OFFLINE_PREFIX) === -1 ) {
                // When it doesn't match any condition, delete it.
                console.info('SW: deleting ' + cacheName);
                return caches.delete(cacheName);
              }
            })
          );
        })
      );
      return self.clients.claim();
    });
    

    Service Worker 库

    参考

    相关文章

      网友评论

        本文标题:Service Worker

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