彩世界平台-彩世界时时app-彩世界开奖app苹果下载

热门关键词: 彩世界平台,彩世界时时app,彩世界开奖app苹果下载

您的位置:彩世界平台 > 学会党委 > Service Worker 从入门到进阶

Service Worker 从入门到进阶

发布时间:2019-11-09 04:58编辑:学会党委浏览(62)

    Service Worker入门

    2015/03/26 · JavaScript · Service Worker

    原文出处: Matt Gaunt   译文出处:[w3ctech

    • 十年踪迹]()   

    原生App拥有Web应用通常所不具备的富离线体验,定时的静默更新,消息通知推送等功能。而新的Service workers标准让在Web App上拥有这些功能成为可能。

    image

    Service Worker 是什么?

    一个 service worker 是一段运行在浏览器后台进程里的脚本,它独立于当前页面,提供了那些不需要与web页面交互的功能在网页背后悄悄执行的能力。在将来,基于它可以实现消息推送,静默更新以及地理围栏等服务,但是目前它首先要具备的功能是拦截和处理网络请求,包括可编程的响应缓存管理。

    为什么说这个API是一个非常棒的API呢?因为它使得开发者可以支持非常好的离线体验,它给予开发者完全控制离线数据的能力。

    在service worker提出之前,另外一个提供开发者离线体验的API叫做App Cache。然而App Cache有些局限性,例如它可以很容易地解决单页应用的问题,但是在多页应用上会很麻烦,而Service workers的出现正是为了解决App Cache的痛点。

    下面详细说一下service worker有哪些需要注意的地方:

    • 它是JavaScript Worker,所以它不能直接操作DOM。但是service worker可以通过postMessage与页面之间通信,把消息通知给页面,如果需要的话,让页面自己去操作DOM。
    • Service worker是一个可编程的网络代理,允许开发者控制页面上处理的网络请求。
    • 在不被使用的时候,它会自己终止,而当它再次被用到的时候,会被重新激活,所以你不能依赖于service worker的onfecth和onmessage的处理函数中的全局状态。如果你想要保存一些持久化的信息,你可以在service worker里使用IndexedDB API。
    • Service worker大量使用promise,所以如果你不了解什么是promise,那你需要先阅读这篇文章。

    特别简的介

    去年开始火遍南北的 PWA 技术落地情况有负重望,主要源于 safrai 对于这一技术支持不甚理想,不支持 mainfest 文件也不支持 service Worker

    service worker 是一个特殊的 web Worker,因此他与页面通信和 worker 是一样的,同样不能访问 DOM。特殊在于他是由事件驱动的具有生命周期的 worker,并且可以拦截处理页面的网络请求(fetch),可以访问 cacheIndexDB彩世界平台,。

    换言之 service Worker 可以让开发者自己控制管理缓存的内容以及版本,为离线弱网环境下的 web 的运行提供了可能,让 web 在体验上更加贴近 native。

    Service Worker的生命周期

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

    要让一个service worker在你的网站上生效,你需要先在你的网页中注册它。注册一个service worker之后,浏览器会在后台默默启动一个service worker的安装过程。

    在安装过程中,浏览器会加载并缓存一些静态资源。如果所有的文件被缓存成功,service worker就安装成功了。如果有任何文件加载或缓存失败,那么安装过程就会失败,service worker就不能被激活(也即没能安装成功)。如果发生这样的问题,别担心,它会在下次再尝试安装。

    当安装完成后,service worker的下一步是激活,在这一阶段,你还可以升级一个service worker的版本,具体内容我们会在后面讲到。

    在激活之后,service worker将接管所有在自己管辖域范围内的页面,但是如果一个页面是刚刚注册了service worker,那么它这一次不会被接管,到下一次加载页面的时候,service worker才会生效。

    当service worker接管了页面之后,它可能有两种状态:要么被终止以节省内存,要么会处理fetch和message事件,这两个事件分别产生于一个网络请求出现或者页面上发送了一个消息。

    下图是一个简化了的service worker初次安装的生命周期:

    彩世界时时app 1

    兼容情况

    safrai 已经于 2017年8月 开始了 service Worker 的开发。

    image

    目前浏览器PC支持情况如图

    国内主要浏览器支持情况

    android 设备在 4.4 版本使用 Chromium 作为内核,Chromium 在 40 对于 service worker 支持。国内浏览器包括微信浏览器在内基本已经支持 service Worker 这为提升体验提供了可能。service workerHTTP2 更加配哦,在将来基于它可以实现消息推送,静默更新以及地理围栏等服务。

    在我们开始写码之前

    从这个项目地址拿到chaches polyfill。

    这个polyfill支持CacheStorate.match,Cache.add和Cache.addAll,而现在Chrome M40实现的彩世界时时app,Cache API还没有支持这些方法。

    将dist/serviceworker-cache-polyfill.js放到你的网站中,在service worker中通过importScripts加载进来。被service worker加载的脚本文件会被自动缓存。

    JavaScript

    importScripts('serviceworker-cache-polyfill.js');

    1
    importScripts('serviceworker-cache-polyfill.js');

    需要HTTPS

    在开发阶段,你可以通过localhost使用service worker,但是一旦上线,就需要你的server支持HTTPS。

    彩世界开奖app苹果下载,你可以通过service worker劫持连接,伪造和过滤响应,非常逆天。即使你可以约束自己不干坏事,也会有人想干坏事。所以为了防止别人使坏,你只能在HTTPS的网页上注册service workers,这样我们才可以防止加载service worker的时候不被坏人篡改。(因为service worker权限很大,所以要防止它本身被坏人篡改利用——译者注)

    Github Pages正好是HTTPS的,所以它是一个理想的天然实验田。

    如果你想要让你的server支持HTTPS,你需要为你的server获得一个TLS证书。不同的server安装方法不同,阅读帮助文档并通过Mozilla’s SSL config generator了解最佳实践。

    了解前的了解

    webWorker
    fetch
    cache
    promise

    使用Service Worker

    现在我们有了polyfill,并且搞定了HTTPS,让我们看看究竟怎么用service worker。

    生命周期

    image

    Service Workermain.js 进行注册,首次注册前会进行分析,判断加载的文件是否在域名下,协议是否为 HTTPS 的,通过这两点则成功注册。
    service Worker 开始进入下一个生命周期状态 installinstall 完成后会触发 service Workerinstall 事件。 如果 install 成功则接下来是 activate状态, 然后这个 service worker 才能接管页面。当事件 active 事件执行完成之后,此时 service Worker 有两种状态,一种是 active,一种是 terminatedactive 是为了工作,terminated则为了节省内存。当新的 service Worker 处于 install/waitting 阶段,当前 service Worker 处于 terminated,就会发生交接替换。或者可以通过调用 self.skipWaiting() 方法跳过等待。
    被替换掉的原有的 service WorkerRedundant 阶段,在 install 或者 activating 中断的也会进入 Redundant 阶段。所以一个 Service Worker 脚本的生命周期有这样一些阶段(从左往右):

    [图片上传失败...(image-af3cfa-1511157771617)]

    如何注册和安装service worker

    要安装service worker,你需要在你的页面上注册它。这个步骤告诉浏览器你的service worker脚本在哪里。

    JavaScript

    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); }); }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    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是否可用,如果可用,service worker /sw.js 被注册。

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

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

    现在你可以到 chrome://inspect/#service-workers 检查service worker是否对你的网站启用了。

    彩世界时时app 2

    当service worker第一版被实现的时候,你也可以在chrome://serviceworker-internals中查看,它很有用,通过它可以最直观地熟悉service worker的生命周期,不过这个功能很快就会被移到chrome://inspect/#service-workers中。

    你会发现这个功能能够很方便地在一个模拟窗口中测试你的service worker,这样你可以关闭和重新打开它,而不会影响到你的新窗口。任何创建在模拟窗口中的注册服务和缓存在窗口被关闭时都将消失。

    Install

    install 存在中间态 installing 这个状态在 main.jsregistration注册对象中可以访问到。

    /* In main.js */
    // 重写 service worker 作用域到 ./
    navigator.serviceWorker.register('./sw.js', {scope: './'}).then(function(registration) {  
        if (registration.installing) {
            // Service Worker is Installing
        }
    })
    

    安装时 service Workerinstall 事件被触发,这一般用于处理静态资源的缓存

    service worker 缓存的静态资源

    chrome PWA 演示实例

    /* In sw.js */
    self.addEventListener('install', function(event) {  
      event.waitUntil(
      // currentCacheName 对应调试工具中高亮位置,缓存的名称
      // 调用 `cache.open` 方法后才可以缓存文件
        caches.open(currentCacheName).then(function(cache) {
        // arrayOfFilesToCache 为存放缓存文件的数组
          return cache.addAll(arrayOfFilesToCache);
        })
      );
    });
    

    event.waitUntil() 方法接收一个 promise 对象, 如果这个 promise 对象 rejectedservice Worker 安装失败,状态变更为 Redundant。关于 cache 相关说明看下文。

    Service Worker的安装步骤

    在页面上完成注册步骤之后,让我们把注意力转到service worker的脚本里来,在这里面,我们要完成它的安装步骤。

    在最基本的例子中,你需要为install事件定义一个callback,并决定哪些文件你想要缓存。

    JavaScript

    // The files we want to cache var urlsToCache = [ '/', '/styles/main.css', '/script/main.js' ]; // Set the callback for the install step self.addEventListener('install', function(event) { // Perform install steps });

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    // The files we want to cache
    var urlsToCache = [
      '/',
      '/styles/main.css',
      '/script/main.js'
    ];
     
    // Set the callback for the install step
    self.addEventListener('install', function(event) {
        // Perform install steps
    });

    在我们的install callback中,我们需要执行以下步骤:

    1. 开启一个缓存
    2. 缓存我们的文件
    3. 决定是否所有的资源是否要被缓存

    JavaScript

    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); }) ); });

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    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);
          })
      );
    });

    上面的代码中,我们通过caches.open打开我们指定的cache文件名,然后我们调用cache.addAll并传入我们的文件数组。这是通过一连串promise(caches.open 和 cache.addAll)完成的。event.waitUntil拿到一个promise并使用它来获得安装耗费的时间以及是否安装成功。

    如果所有的文件都被缓存成功了,那么service worker就安装成功了。如果任何一个文件下载失败,那么安装步骤就会失败。这个方式允许你依赖于你自己指定的所有资源,但是这意味着你需要非常谨慎地决定哪些文件需要在安装步骤中被缓存。指定了太多的文件的话,就会增加安装失败率。

    上面只是一个简单的例子,你可以在install事件中执行其他操作或者甚至忽略install事件。

    Installed / Waiting

    安装完成待正在运行的 service Worker 交接的状态。
    Service Worker registration 对象, 我们可以获得这个状态

    /* In main.js */
    navigator.serviceWorker.register('./sw.js').then(function(registration) {  
        if (registration.waiting) {
            // Service Worker is Waiting
        }
    })
    

    这是一个提示用户更新的好时机,或者可以静默更新。

    怎样缓存和返回Request

    你已经安装了service worker,你现在可以返回你缓存的请求了。

    当service worker被安装成功并且用户浏览了另一个页面或者刷新了当前的页面,service worker将开始接收到fetch事件。下面是一个例子:

    JavaScript

    self.addEventListener('fetch', function(event) { event.respondWith( caches.match(event.request) .then(function(response) { // Cache hit - return response if (response) { return response; } return fetch(event.request); } ) ); });

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    self.addEventListener('fetch', function(event) {
      event.respondWith(
        caches.match(event.request)
          .then(function(response) {
            // Cache hit - return response
            if (response) {
              return response;
            }
     
            return fetch(event.request);
          }
        )
      );
    });

    上面的代码里我们定义了fetch事件,在event.respondWith里,我们传入了一个由caches.match产生的promise.caches.match 查找request中被service worker缓存命中的response。

    如果我们有一个命中的response,我们返回被缓存的值,否则我们返回一个实时从网络请求fetch的结果。这是一个非常简单的例子,使用所有在install步骤下被缓存的资源。

    如果我们想要增量地缓存新的请求,我们可以通过处理fetch请求的response并且添加它们到缓存中来实现,例如:

    JavaScript

    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; } ); }) ); });

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    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;
              }
            );
          })
        );
    });

    代码里我们所做事情包括:

    1. 添加一个callback到fetch请求的 .then 方法中
    2. 一旦我们获得了一个response,我们进行如下的检查:
      1. 确保response是有效的
      2. 检查response的状态是否是200
      3. 保证response的类型是basic,这表示请求本身是同源的,非同源(即跨域)的请求也不能被缓存。
    3. 如果我们通过了检查,clone这个请求。这么做的原因是如果response是一个Stream,那么它的body只能被读取一次,所以我们得将它克隆出来,一份发给浏览器,一份发给缓存。

    Activating

    • 当页面没有正在运行的 service Worker时;
    • service Worker脚本中调用了 self.skipWaiting 方法;
    • 用户切换页面使原有的 service Worker 释放;
    • 特定失效已过,释放因此原有的 service Worker 被释放

    则状态变为 activating,触发 service workeractive 事件。

    /* In sw.js */
    self.addEventListener('activate', function(event) {  
      event.waitUntil(
        // Get all the cache names
        caches.keys().then(function(cacheNames) {
          return Promise.all(
            // Get all the items that are stored under a different cache name than the current one
            cacheNames.filter(function(cacheName) {
              return cacheName != currentCacheName;
            }).map(function(cacheName) {
              // Delete the items
              return caches.delete(cacheName);
            })
          ); // end Promise.all()
        }) // end caches.keys()
      ); // end event.waitUntil()
    });
    

    install 事件中的 event.waitUntil 方法。当所接收的 promisereject 那么 serviceWorker 进入 Redundant状态。

    如何更新一个Service Worker

    你的service worker总有需要更新的那一天。当那一天到来的时候,你需要按照如下步骤来更新:

    1. 更新你的service worker的JavaScript文件
      1. 当用户浏览你的网站,浏览器尝试在后台下载service worker的脚本文件。只要服务器上的文件和本地文件有一个字节不同,它们就被判定为需要更新。
    2. 更新后的service worker将开始运作,install event被重新触发。
    3. 在这个时间节点上,当前页面生效的依然是老版本的service worker,新的servicer worker将进入”waiting”状态。
    4. 当前页面被关闭之后,老的service worker进程被杀死,新的servicer worker正式生效。
    5. 一旦新的service worker生效,它的activate事件被触发。

    代码更新后,通常需要在activate的callback中执行一个管理cache的操作。因为你会需要清除掉之前旧的数据。我们在activate而不是install的时候执行这个操作是因为如果我们在install的时候立马执行它,那么依然在运行的旧版本的数据就坏了。

    之前我们只使用了一个缓存,叫做my-site-cache-v1,其实我们也可以使用多个缓存的,例如一个给页面使用,一个给blog的内容提交使用。这意味着,在install步骤里,我们可以创建两个缓存,pages-cache-v1和blog-posts-cache-v1,在activite步骤里,我们可以删除旧的my-site-cache-v1。

    下面的代码能够循环所有的缓存,删除掉所有不在白名单中的缓存。

    JavaScript

    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); } }) ); }) ); });

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    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);
              }
            })
          );
        })
      );
    });

    Actived

    activting成功后,这时 service Worker 接管了整个页面状态变为 acticed
    这个状态我们可以拦截请求和消息。

    /* In sw.js */
    
    self.addEventListener('fetch', function(event) {  
      // Do stuff with fetch events
    });
    
    self.addEventListener('message', function(event) {  
      // Do stuff with postMessages received from document
    });
    

    处理边界和填坑

    这一节内容比较新,有很多待定细节。希望这一节很快就不需要讲了(因为标准会处理这些问题——译者注),但是现在,这些内容还是应该被提一下。

    Redundant

    service Workerinstall active过程中处错误或者,被新的 service Worker 替换状态会变为 Redundant

    如果是后一种情况,则该 worker 仍然控制这个页面。

    值得注意的是已经 installservice worker 页面关闭后再打开不会触发 install 事件,但是会重新注册。更多参考文章 探索 Service Worker 「生命周期」

    如果安装失败了,没有很优雅的方式获得通知

    如果一个worker被注册了,但是没有出现在chrome://inspect/#service-workers或chrome://serviceworker-internals,那么很可能因为异常而安装失败了,或者是产生了一个被拒绝的的promise给event.waitUtil。

    要解决这类问题,首先到 chrome://serviceworker-internals检查。打开开发者工具窗口准备调试,然后在你的install event代码中添加debugger;语句。这样,通过断点调试你更容易找到问题。

    请求处理

    处于 actived 阶段的 service Worker 可以拦截页面发出的 fetch,也可以发出fetch请求,可以将请求和响应缓存在 cache里,也可以将 responsecache 中取出。

    本文由彩世界平台发布于学会党委,转载请注明出处:Service Worker 从入门到进阶

    关键词: