workers标准让在Web App上拥有这些功能成为可能

Service Worker入门

2015/03/26 · JavaScript · Service Worker

初稿出处: Matt Gaunt   译文出处:[w3ctech

  • 十年踪迹]()   

原生App具备Web应用通常所不富有的富离线体验,定期的沉默更新,音信布告推送等功用。而新的Serviceworkers标准让在Web App上富有这么些意义成为恐怕。

Service Worker 是什么?

四个 service worker 是后生可畏段运维在浏览器后台进度里的台本,它独立于如今页面,提供了那多少个不要求与web页面交互的机能在网页背后悄悄试行的力量。在以往,基于它能够完毕音讯推送,静默更新以至地理围栏等劳务,可是如今它首先要具备的功能是阻挠和处理互联网须求,富含可编制程序的响应缓存管理。

干什么说那个API是一个非常棒的API呢?因为它使得开发者能够帮助相当好的离线体验,它付与开垦者完全调控离线数据的才具。

在service worker提出以前,其余四个提供开拓者离线体验的API叫做App Cache。但是App Cache某些局限性,举个例子它可以十分轻松地消除单页应用的标题,但是在多页应用上会很麻烦,而Serviceworkers的现身正是为了解决App Cache的痛点。

下边详细说一下service worker有啥须要专心的地点:

  • 它是JavaScript Worker,所以它不能够一贯操作DOM。可是service worker能够因此postMessage与页面之间通讯,把音信文告给页面,若是须求的话,让页面自身去操作DOM。
  • Serviceworker是二个可编制程序的互连网代理,允许开辟者调控页面上管理的网络央浼。
  • 在不被使用的时候,它会融洽终止,而当它再也被用到的时候,会被再度激活,所以你不能够依附于service worker的onfecth和onmessage的管理函数中的全局状态。如若你想要保存一些长久化的音信,你能够在service worker里使用IndexedDB API。
  • Serviceworker大批量使用promise,所以若是您不领悟什么是promise,那你须要先读书这篇文章。

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初次安装的生命周期:

图片 1

在大家最初写码从前

从这个种类地址拿到chaches polyfill。

这个polyfill支持CacheStorate.match,Cache.add和Cache.addAll,而现在Chrome M40实现的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。

您可以通过service worker勒迫连接,虚商谈过滤响应,极其逆天。固然你能够限制本人不干坏事,也可以有人想干坏事。所以为了防卫别人使坏,你必须要在HTTPS的网页上注册service workers,那样大家手艺够免止加载service worker的时候不被歹徒窜改。(因为service worker权限相当大,所以要防患未然它自个儿被人渣点窜利用——译者注)

Github Pages无独有偶是HTTPS的,所以它是三个美好的天然实验田。

假定你想要令你的server援助HTTPS,你要求为您的server获得叁个TLS证书。区别的server安装方法差别,阅读帮衬文书档案并透过Mozilla’s SSL config generator摸底最棒执行。

使用Service Worker

今昔大家有了polyfill,何况解决了HTTPS,让大家看见到底怎么用service worker。

什么注册和安装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是否对你的网站启用了。

图片 2

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

你会开采那一个作用可以很有益地在叁个模拟窗口中测量试验你的service worker,这样您能够关闭和另行张开它,而不会影响到你的新窗口。任何创制在模拟窗口中的注册服务和缓存在窗口被关闭时都将无影无踪。

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事件。

怎样缓存和重回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只可以被读取贰遍,所以大家得将它克隆出来,豆蔻梢头份发给浏览器,生机勃勃份发给缓存。

什么改革二个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);
          }
        })
      );
    })
  );
});

管理边界和填坑

这风姿洒脱节内容相比新,有数不清待定细节。希望那风流洒脱节异常快就不要求讲了(因为标准会管理这个难题——译者注),然则将来,这一个内容还是应该被提一下。

设若设置退步了,未有相当高雅的法子获取通报

要是三个worker被登记了,可是尚未出今后chrome://inspect/#service-workers或chrome://serviceworker-internals,那么很可能因为异常而安装失败了,或者是产生了一个被拒绝的的promise给event.waitUtil。

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

fetch()如今仅帮助Service Workers

fetch即刻帮忙在页面上使用了,可是当前的Chrome完成,它还只帮忙service worker。cache API也将要要页面上被扶植,不过方今截至,cache也还必须要在service worker中用。

fetch()的暗中认可参数

当您利用fetch,缺省级地区级,要求不会带上cookies等证据,要想带上的话,须要:

JavaScript

fetch(url, { credentials: 'include' })

1
2
3
fetch(url, {
  credentials: 'include'
})

这么设计是有理由的,它比XHPRADO的在同源下默许发送凭据,但跨域时遗弃凭据的准则要来得好。fetch的表现更像此外的CO大切诺基S央浼,举例<img crossorigin>,它默认不发送cookies,除非你指定了<img crossorigin="use-credentials">.。

Non-COPRADOS私下认可不协助

暗许境况下,从第三方UOdysseyL跨域得到贰个能源将会战败,除非对方援助了COPRADOS。你能够加上一个non-CO奥德赛S选项到Request去防止战败。代价是如此做会回去叁个“不透明”的response,意味着你无法识破那几个恳求毕竟是成功了只怕退步了。

JavaScript

cache.addAll(urlsToPrefetch.map(function(urlToPrefetch) { return new Request(urlToPrefetch, { mode: 'no-cors' }); })).then(function() { console.log('All resources have been fetched and cached.'); });

1
2
3
4
5
cache.addAll(urlsToPrefetch.map(function(urlToPrefetch) {
  return new Request(urlToPrefetch, { mode: 'no-cors' });
})).then(function() {
  console.log('All resources have been fetched and cached.');
});

fetch()不坚决守护30x重定向标准

噩运,重定向在fetch()中不会被触发,那是当前版本的bug;

拍卖响应式图片

img的srcset属性恐怕<picture>标签会根据情况从浏览器或者网络上选择最合适尺寸的图片。

在service worker中,你想要在install步骤缓存三个图片,你有以下二种选用:

  1. 安装具有的<picture>元素或者将被请求的srcset属性。
  2. 安装单风度翩翩的low-res版本图片
  3. 设置单少年老成的high-res版本图片

相比较好的方案是2或3,因为只要把具备的图纸都给下载下来存着有一些浪费内部存款和储蓄器。

假如你将low-res版本在install的时候缓存了,然后在页面加载的时候你想要尝试从互连网上下载high-res的版本,可是倘使high-res版本下载退步以来,就像故用low-res版本。这么些主见很好也值得去做,可是有二个难点:

借使我们有下边三种图片:

Screen Density Width Height
1x 400 400
2x 800 800

HTML代码如下:

JavaScript

<img src="image-src.png" srcset="image-src.png 1x, image-2x.png 2x" />

1
<img src="image-src.png" srcset="image-src.png 1x, image-2x.png 2x" />

假定大家在一个2x的显示形式下,浏览器会下载image-2x.png,假若大家离线,你能够读取从前缓存并重回image-src.png替代,假使在此之前它曾经被缓存过。固然如此,由于几日前的格局是2x,浏览器会把400X400的图形展现成200X200,要防止那么些主题材料将要在图片的样式上安装宽高。

JavaScript

<img src="image-src.png" srcset="image-src.png 1x, image-2x.png 2x" style="width:400px; height: 400px;" />

1
2
<img src="image-src.png" srcset="image-src.png 1x, image-2x.png 2x"
style="width:400px; height: 400px;" />

图片 3

<picture>标签情况更复杂一些,难度取决于你是如何创建和使用的,但是可以通过与srcset类似的思路去解决。

改变URL Hash的Bug

在M40版本中设有二个bug,它会让页面在转移hash的时候变成service worker甘休工作。

你能够在这里间找到越来越多相关的消息: 

越来越多内容

此地有生机勃勃部分生死相依的文书档案能够参见:

获得救助

若果你遇上麻烦,请在Stackoverflow上发帖询问,使用‘service-worker’标签,以便于大家马上跟进和不择花招扶助您消除难题。

赞 2 收藏 评论

图片 4

本文由快3平台发布于安徽快三web前端,转载请注明出处:workers标准让在Web App上拥有这些功能成为可能

相关阅读